summaryrefslogtreecommitdiffstats
path: root/private/lsa
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/lsa
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/lsa')
-rw-r--r--private/lsa/client/austub.c883
-rw-r--r--private/lsa/client/autest1.c165
-rw-r--r--private/lsa/client/dirs25
-rw-r--r--private/lsa/client/kernel/makefile6
-rw-r--r--private/lsa/client/kernel/sources36
-rw-r--r--private/lsa/client/lsadllp.h29
-rw-r--r--private/lsa/client/ssp/connmgr.c271
-rw-r--r--private/lsa/client/ssp/connmgr.h67
-rw-r--r--private/lsa/client/ssp/context.c146
-rw-r--r--private/lsa/client/ssp/ksecdd.c440
-rw-r--r--private/lsa/client/ssp/ksecdd.def25
-rw-r--r--private/lsa/client/ssp/ksecdd.h102
-rw-r--r--private/lsa/client/ssp/makefile28
-rw-r--r--private/lsa/client/ssp/memmgr.c192
-rw-r--r--private/lsa/client/ssp/memmgr.h30
-rw-r--r--private/lsa/client/ssp/ntlm.c395
-rw-r--r--private/lsa/client/ssp/package.h81
-rw-r--r--private/lsa/client/ssp/res.rc11
-rw-r--r--private/lsa/client/ssp/sources50
-rw-r--r--private/lsa/client/ssp/sspdrv.h37
-rw-r--r--private/lsa/client/ssp/stubs.c1565
-rw-r--r--private/lsa/client/ssp/support.c432
-rw-r--r--private/lsa/client/ssp/userstub.c460
-rw-r--r--private/lsa/client/user/makefile6
-rw-r--r--private/lsa/client/user/sources36
-rw-r--r--private/lsa/common/cr.c403
-rw-r--r--private/lsa/common/lsaprtl.c1158
-rw-r--r--private/lsa/common/lsarpcmm.c98
-rw-r--r--private/lsa/common/makefile6
-rw-r--r--private/lsa/common/sources44
-rw-r--r--private/lsa/crypt/dirs26
-rw-r--r--private/lsa/crypt/dll/block.c235
-rw-r--r--private/lsa/crypt/dll/data.c762
-rw-r--r--private/lsa/crypt/dll/data2.c105
-rw-r--r--private/lsa/crypt/dll/makefile6
-rw-r--r--private/lsa/crypt/dll/makefile.inc3
-rw-r--r--private/lsa/crypt/dll/owf.c212
-rw-r--r--private/lsa/crypt/dll/owfcrypt.c782
-rw-r--r--private/lsa/crypt/dll/response.c153
-rw-r--r--private/lsa/crypt/dll/sources43
-rw-r--r--private/lsa/crypt/dll/userkey.c832
-rw-r--r--private/lsa/crypt/engine/descrypt.h41
-rw-r--r--private/lsa/crypt/engine/engine.h31
-rw-r--r--private/lsa/crypt/engine/makefile6
-rw-r--r--private/lsa/crypt/engine/md2.h33
-rw-r--r--private/lsa/crypt/engine/md4.h48
-rw-r--r--private/lsa/crypt/engine/md4c.c223
-rw-r--r--private/lsa/crypt/engine/md5.h69
-rw-r--r--private/lsa/crypt/engine/obj/i386/rsa32.foobin0 -> 265548 bytes
-rw-r--r--private/lsa/crypt/engine/rc4.h10
-rw-r--r--private/lsa/crypt/engine/rsa.h272
-rw-r--r--private/lsa/crypt/engine/sha.h32
-rw-r--r--private/lsa/crypt/engine/shacomm.h30
-rw-r--r--private/lsa/crypt/engine/sources39
-rw-r--r--private/lsa/crypt/test/ctauto.c820
-rw-r--r--private/lsa/crypt/test/ctmanual.c205
-rw-r--r--private/lsa/crypt/test/ctutil.c384
-rw-r--r--private/lsa/crypt/test/ctutil.h161
-rw-r--r--private/lsa/crypt/test/makefile6
-rw-r--r--private/lsa/crypt/test/sources44
-rw-r--r--private/lsa/dirs33
-rw-r--r--private/lsa/inc/aup.h217
-rw-r--r--private/lsa/inc/cr.h131
-rw-r--r--private/lsa/inc/lsacomp.h65
-rw-r--r--private/lsa/inc/lsaprtl.h151
-rw-r--r--private/lsa/inc/ntlsa.bak2240
-rw-r--r--private/lsa/inc/rpcutil.h76
-rw-r--r--private/lsa/lsacli.acf90
-rw-r--r--private/lsa/lsaimp.idl46
-rw-r--r--private/lsa/lsarpc.idl1066
-rw-r--r--private/lsa/lsasrv.acf63
-rw-r--r--private/lsa/makefil085
-rw-r--r--private/lsa/msprivs/makefile6
-rw-r--r--private/lsa/msprivs/mspr_rev.rc11
-rw-r--r--private/lsa/msprivs/msprivs.def7
-rw-r--r--private/lsa/msprivs/msprivs.xlsbin0 -> 32256 bytes
-rw-r--r--private/lsa/msprivs/msprivs2.rcbin0 -> 71929 bytes
-rw-r--r--private/lsa/msprivs/mstmp.c58
-rw-r--r--private/lsa/msprivs/sources67
-rw-r--r--private/lsa/msprivs/tprivs.c581
-rw-r--r--private/lsa/msv1_0/makefile6
-rw-r--r--private/lsa/msv1_0/msp.h334
-rw-r--r--private/lsa/msv1_0/msv1_0.c456
-rw-r--r--private/lsa/msv1_0/msv1_0.def15
-rw-r--r--private/lsa/msv1_0/msv1_0.prf173
-rw-r--r--private/lsa/msv1_0/msv1_0.rc38
-rw-r--r--private/lsa/msv1_0/msvars.c76
-rw-r--r--private/lsa/msv1_0/msvpaswd.c1690
-rw-r--r--private/lsa/msv1_0/msvsam.c2091
-rw-r--r--private/lsa/msv1_0/nlmain.c3303
-rw-r--r--private/lsa/msv1_0/nlnetapi.c263
-rw-r--r--private/lsa/msv1_0/nlp.c1822
-rw-r--r--private/lsa/msv1_0/nlp.h473
-rw-r--r--private/lsa/msv1_0/nlpcache.c4561
-rw-r--r--private/lsa/msv1_0/nlpcache.h184
-rw-r--r--private/lsa/msv1_0/nlvars.c86
-rw-r--r--private/lsa/msv1_0/pwdtest.c401
-rw-r--r--private/lsa/msv1_0/sources76
-rw-r--r--private/lsa/msv1_0/subauth.c447
-rw-r--r--private/lsa/msv1_0/subauth/makefile6
-rw-r--r--private/lsa/msv1_0/subauth/sources54
-rw-r--r--private/lsa/msv1_0/subauth/subauth.c1354
-rw-r--r--private/lsa/msv1_0/subauth/subauth.def8
-rw-r--r--private/lsa/server/adt.h155
-rw-r--r--private/lsa/server/adtbuild.c624
-rw-r--r--private/lsa/server/adtevent.c636
-rw-r--r--private/lsa/server/adtinit.c878
-rw-r--r--private/lsa/server/adtlog.c3687
-rw-r--r--private/lsa/server/adtobjs.c1364
-rw-r--r--private/lsa/server/adtp.h493
-rw-r--r--private/lsa/server/au.h28
-rw-r--r--private/lsa/server/auclient.c268
-rw-r--r--private/lsa/server/aucred.c840
-rw-r--r--private/lsa/server/auctxt.c456
-rw-r--r--private/lsa/server/aufilter.c2009
-rw-r--r--private/lsa/server/auinit.c329
-rw-r--r--private/lsa/server/aulogon.c2120
-rw-r--r--private/lsa/server/auloop.c1500
-rw-r--r--private/lsa/server/aumsp.c141
-rw-r--r--private/lsa/server/aupckg.c792
-rw-r--r--private/lsa/server/auproc.c442
-rw-r--r--private/lsa/server/ausess.c953
-rw-r--r--private/lsa/server/ausrvp.h889
-rw-r--r--private/lsa/server/crserver.c113
-rw-r--r--private/lsa/server/ctlsarm.c244
-rw-r--r--private/lsa/server/ctlsarpc.c15305
-rw-r--r--private/lsa/server/db.h983
-rw-r--r--private/lsa/server/dbaccnt.c4017
-rw-r--r--private/lsa/server/dbadmin.c947
-rw-r--r--private/lsa/server/dbattr.c839
-rw-r--r--private/lsa/server/dbdata.c436
-rw-r--r--private/lsa/server/dbdomain.c4325
-rw-r--r--private/lsa/server/dbhandle.c994
-rw-r--r--private/lsa/server/dbinit.c2821
-rw-r--r--private/lsa/server/dbinstac.c643
-rw-r--r--private/lsa/server/dbinstal.c1335
-rw-r--r--private/lsa/server/dblookup.c13138
-rw-r--r--private/lsa/server/dblookup.h915
-rw-r--r--private/lsa/server/dbmisc.c545
-rw-r--r--private/lsa/server/dbobject.c3965
-rw-r--r--private/lsa/server/dbp.h870
-rw-r--r--private/lsa/server/dbpob.c696
-rw-r--r--private/lsa/server/dbpolicy.c4006
-rw-r--r--private/lsa/server/dbpriv.c2572
-rw-r--r--private/lsa/server/dbsamtst.c1062
-rw-r--r--private/lsa/server/dbsecret.c1750
-rw-r--r--private/lsa/server/dirs1
-rw-r--r--private/lsa/server/dll/makefile6
-rw-r--r--private/lsa/server/dll/sources109
-rw-r--r--private/lsa/server/exe/makefile6
-rw-r--r--private/lsa/server/exe/sources57
-rw-r--r--private/lsa/server/lsa_rev.rc11
-rw-r--r--private/lsa/server/lsaerror.c64
-rw-r--r--private/lsa/server/lsaifree.c552
-rw-r--r--private/lsa/server/lsainit.c843
-rw-r--r--private/lsa/server/lsapmsgs.mc147
-rw-r--r--private/lsa/server/lsarm.c660
-rw-r--r--private/lsa/server/lsasrv.def104
-rw-r--r--private/lsa/server/lsasrv.prf254
-rw-r--r--private/lsa/server/lsasrvmm.c707
-rw-r--r--private/lsa/server/lsasrvmm.h125
-rw-r--r--private/lsa/server/lsasrvp.h496
-rw-r--r--private/lsa/server/lsass.c543
-rw-r--r--private/lsa/server/lsawrap.c2769
-rw-r--r--private/lsa/server/main.c100
-rw-r--r--private/lsa/server/makefile.inc2
-rw-r--r--private/lsa/server/oldstub.c399
-rw-r--r--private/lsa/server/regnames.txt123
-rw-r--r--private/lsa/server/rpcinit.c193
-rw-r--r--private/lsa/server/sepriv.c519
-rw-r--r--private/lsa/server/services.c458
-rw-r--r--private/lsa/server/tadt1.c454
-rw-r--r--private/lsa/uclient/crclient.c112
-rw-r--r--private/lsa/uclient/ctlkacct.c378
-rw-r--r--private/lsa/uclient/ctlklsa.c579
-rw-r--r--private/lsa/uclient/ctreg.c807
-rw-r--r--private/lsa/uclient/ctsamdb.c213
-rw-r--r--private/lsa/uclient/lsaclip.h25
-rw-r--r--private/lsa/uclient/lsaudll.def11
-rw-r--r--private/lsa/uclient/makefile6
-rw-r--r--private/lsa/uclient/rpcapi.c3797
-rw-r--r--private/lsa/uclient/rpcapi2.c3316
-rw-r--r--private/lsa/uclient/rpcbind.c102
-rw-r--r--private/lsa/uclient/rpcclimm.c103
-rw-r--r--private/lsa/uclient/runsamdb.cmd4
-rw-r--r--private/lsa/uclient/sources44
-rw-r--r--private/lsa/uclient/tgetsid.c211
-rw-r--r--private/lsa/uclient/tlookup.c249
-rw-r--r--private/lsa/uclient/tsecomm.c136
-rw-r--r--private/lsa/uclient/tsevars.c447
190 files changed, 137117 insertions, 0 deletions
diff --git a/private/lsa/client/austub.c b/private/lsa/client/austub.c
new file mode 100644
index 000000000..e8140959c
--- /dev/null
+++ b/private/lsa/client/austub.c
@@ -0,0 +1,883 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ austub.c
+
+Abstract:
+
+ Local Security Authority AUTHENTICATION service client stubs.
+
+Author:
+
+ Jim Kelly (JimK) 20-Feb-1991
+
+Environment: Kernel or User Modes
+
+Revision History:
+
+--*/
+
+#include "lsadllp.h"
+#include <string.h>
+#include <zwapi.h>
+
+
+NTSTATUS
+LsaFreeReturnBuffer (
+ IN PVOID Buffer
+ )
+
+
+/*++
+
+Routine Description:
+
+ Some of the LSA authentication services allocate memory buffers to
+ hold returned information. This service is used to free those buffers
+ when no longer needed.
+
+Arguments:
+
+ Buffer - Supplies a pointer to the return buffer to be freed.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ Others - returned by NtFreeVirtualMemory().
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ ULONG Length;
+
+ Length = 0;
+ Status = ZwFreeVirtualMemory(
+ NtCurrentProcess(),
+ &Buffer,
+ &Length,
+ MEM_RELEASE
+ );
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaRegisterLogonProcess(
+ IN PSTRING LogonProcessName,
+ OUT PHANDLE LsaHandle,
+ OUT PLSA_OPERATIONAL_MODE SecurityMode
+ )
+
+/*++
+
+Routine Description:
+
+ This service connects to the LSA server and verifies that the caller
+ is a legitimate logon process. this is done by ensuring the caller has
+ the SeTcbPrivilege privilege. It also opens the caller's process for
+ PROCESS_DUP_HANDLE access in anticipation of future LSA authentication
+ calls.
+
+Arguments:
+
+ LogonProcessName - Provides a name string that identifies the logon
+ process. This should be a printable name suitable for display to
+ administrators. For example, "User32LogonProces" might be used
+ for the windows logon process name. No check is made to determine
+ whether the name is already in use. This name must NOT be longer
+ than 127 bytes long.
+
+ LsaHandle - Receives a handle which must be provided in future
+ authenticaiton services.
+
+ SecurityMode - The security mode the system is running under. This
+ value typically influences the logon user interface. For example,
+ a system running with password control will prompt for username
+ and passwords before bringing up the UI shell. One running without
+ password control would typically automatically bring up the UI shell
+ at system initialization.
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have the
+ privilege necessary to act as a logon process. The SeTcbPrivilege
+ privilege is needed.
+
+
+ STATUS_NAME_TOO_LONG - The logon process name provided is too long.
+
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ UNICODE_STRING PortName, EventName;
+ LSAP_AU_REGISTER_CONNECT_INFO ConnectInfo;
+ ULONG ConnectInfoLength;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE EventHandle;
+
+
+ //
+ // Validate input parameters
+ //
+
+ if (LogonProcessName->Length > LSAP_MAX_LOGON_PROC_NAME_LENGTH) {
+ return STATUS_NAME_TOO_LONG;
+ }
+
+
+ //
+ // Wait for LSA to initialize...
+ //
+
+
+ RtlInitUnicodeString( &EventName, L"\\SECURITY\\LSA_AUTHENTICATION_INITIALIZED" );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &EventName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ NULL
+ );
+
+ Status = ZwOpenEvent( &EventHandle, SYNCHRONIZE, &ObjectAttributes );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = ZwWaitForSingleObject( EventHandle, TRUE, NULL);
+ IgnoreStatus = ZwClose( EventHandle );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port. Use the most efficient (least overhead) - which is dynamic
+ // rather than static tracking.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+
+
+
+ //
+ // Set up the connection information to contain the logon process
+ // name.
+ //
+
+ ConnectInfoLength = sizeof(LSAP_AU_REGISTER_CONNECT_INFO);
+ strncpy(
+ ConnectInfo.LogonProcessName,
+ LogonProcessName->Buffer,
+ LogonProcessName->Length
+ );
+ ConnectInfo.LogonProcessNameLength = LogonProcessName->Length;
+ ConnectInfo.LogonProcessName[ConnectInfo.LogonProcessNameLength] = '\0';
+
+
+ //
+ // Connect to the LSA server
+ //
+
+ RtlInitUnicodeString(&PortName,L"\\LsaAuthenticationPort");
+ Status = ZwConnectPort(
+ LsaHandle,
+ &PortName,
+ &DynamicQos,
+ NULL,
+ NULL,
+ NULL,
+ &ConnectInfo,
+ &ConnectInfoLength
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ //DbgPrint("LSA AU: Logon Process Register failed %lx\n",Status);
+ return Status;
+ }
+
+ if ( !NT_SUCCESS(ConnectInfo.CompletionStatus) ) {
+ //DbgPrint("LSA AU: Logon Process Register rejected %lx\n",ConnectInfo.CompletionStatus);
+ ;
+ }
+
+ (*SecurityMode) = ConnectInfo.SecurityMode;
+
+ return ConnectInfo.CompletionStatus;
+
+}
+
+
+NTSTATUS
+LsaConnectUntrusted(
+ OUT PHANDLE LsaHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This service connects to the LSA server and sets up an untrusted
+ connection. It does not check anything about the caller.
+
+Arguments:
+
+
+ LsaHandle - Receives a handle which must be provided in future
+ authenticaiton services.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ UNICODE_STRING PortName, EventName;
+ LSAP_AU_REGISTER_CONNECT_INFO ConnectInfo;
+ ULONG ConnectInfoLength;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE EventHandle;
+
+
+
+ //
+ // Wait for LSA to initialize...
+ //
+
+
+ RtlInitUnicodeString( &EventName, L"\\SECURITY\\LSA_AUTHENTICATION_INITIALIZED" );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &EventName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ NULL
+ );
+
+ Status = ZwOpenEvent( &EventHandle, SYNCHRONIZE, &ObjectAttributes );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = ZwWaitForSingleObject( EventHandle, TRUE, NULL);
+ IgnoreStatus = ZwClose( EventHandle );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port. Use the most efficient (least overhead) - which is dynamic
+ // rather than static tracking.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+
+
+
+ //
+ // Set up the connection information to contain the logon process
+ // name.
+ //
+
+ ConnectInfoLength = sizeof(LSAP_AU_REGISTER_CONNECT_INFO);
+ RtlZeroMemory(
+ &ConnectInfo,
+ ConnectInfoLength
+ );
+
+
+ //
+ // Connect to the LSA server
+ //
+
+ RtlInitUnicodeString(&PortName,L"\\LsaAuthenticationPort");
+ Status = ZwConnectPort(
+ LsaHandle,
+ &PortName,
+ &DynamicQos,
+ NULL,
+ NULL,
+ NULL,
+ &ConnectInfo,
+ &ConnectInfoLength
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ //DbgPrint("LSA AU: Logon Process Register failed %lx\n",Status);
+ return Status;
+ }
+
+ if ( !NT_SUCCESS(ConnectInfo.CompletionStatus) ) {
+ //DbgPrint("LSA AU: Logon Process Register rejected %lx\n",ConnectInfo.CompletionStatus);
+ ;
+ }
+
+ return ConnectInfo.CompletionStatus;
+
+}
+
+
+NTSTATUS
+LsaLookupAuthenticationPackage (
+ IN HANDLE LsaHandle,
+ IN PSTRING PackageName,
+ OUT PULONG AuthenticationPackage
+ )
+
+/*++
+
+Arguments:
+
+ LsaHandle - Supplies a handle obtained in a previous call to
+ LsaRegisterLogonProcess.
+
+ PackageName - Supplies a string which identifies the
+ Authentication Package. "MSV1.0" is the standard NT
+ authentication package name. The package name must not
+ exceed 127 bytes in length.
+
+ AuthenticationPackage - Receives an ID used to reference the
+ authentication package in subsequent authentication services.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_NO_SUCH_PACKAGE - The specified authentication package is
+ unknown to the LSA.
+
+ STATUS_NAME_TOO_LONG - The authentication package name provided is too
+ long.
+
+
+
+Routine Description:
+
+ This service is used to obtain the ID of an authentication package.
+ This ID may then be used in subsequent authentication services.
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LSAP_AU_API_MESSAGE Message;
+ PLSAP_LOOKUP_PACKAGE_ARGS Arguments;
+
+ //
+ // Validate input parameters
+ //
+
+ if (PackageName->Length > LSAP_MAX_PACKAGE_NAME_LENGTH) {
+ return STATUS_NAME_TOO_LONG;
+ }
+
+
+
+ Arguments = &Message.Arguments.LookupPackage;
+
+ //
+ // Set arguments
+ //
+
+ strncpy(Arguments->PackageName, PackageName->Buffer, PackageName->Length);
+ Arguments->PackageNameLength = PackageName->Length;
+ Arguments->PackageName[Arguments->PackageNameLength] = '\0';
+
+
+
+ //
+ // Call the Local Security Authority Server.
+ //
+
+ Message.ApiNumber = LsapAuLookupPackageApi;
+ Message.PortMessage.u1.s1.DataLength = sizeof(*Arguments) + 8;
+ Message.PortMessage.u1.s1.TotalLength = sizeof(Message);
+ Message.PortMessage.u2.ZeroInit = 0L;
+
+ Status = ZwRequestWaitReplyPort(
+ LsaHandle,
+ (PPORT_MESSAGE) &Message,
+ (PPORT_MESSAGE) &Message
+ );
+
+ //
+ // Return the authentication package ID.
+ // If the call failed for any reason, this will be garbage,
+ // but who cares.
+ //
+
+ (*AuthenticationPackage) = Arguments->AuthenticationPackage;
+
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = Message.ReturnedStatus;
+ if ( !NT_SUCCESS(Status) ) {
+ //DbgPrint("LSA AU: Package Lookup Failed %lx\n",Status);
+ ;
+ }
+ } else {
+#if DBG
+ DbgPrint("LSA AU: Package Lookup NtRequestWaitReply Failed %lx\n",Status);
+#else
+ ;
+#endif
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaLogonUser (
+ IN HANDLE LsaHandle,
+ IN PSTRING OriginName,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG AuthenticationPackage,
+ IN PVOID AuthenticationInformation,
+ IN ULONG AuthenticationInformationLength,
+ IN PTOKEN_GROUPS LocalGroups OPTIONAL,
+ IN PTOKEN_SOURCE SourceContext,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferLength,
+ OUT PLUID LogonId,
+ OUT PHANDLE Token,
+ OUT PQUOTA_LIMITS Quotas,
+ OUT PNTSTATUS SubStatus
+ )
+
+/*++
+
+Arguments:
+
+ LsaHandle - Supplies a handle obtained in a previous call to
+ LsaRegisterLogonProcess.
+
+ OriginName - Supplies a string which identifies the origin of the
+ logon attempt. For example, "TTY1" specify terminal 1, or
+ "LAN Manager - remote node JAZZ" might indicate a network
+ logon attempt via LAN Manager from a remote node called
+ "JAZZ".
+
+ LogonType - Identifies the type of logon being attempted. If the
+ type is Interactive or Batch then a PrimaryToken will be
+ generated to represent this new user. If the type is Network
+ then an impersonation token will be generated.
+
+ AuthenticationPackage - Supplies the ID of the authentication
+ package to use for the logon attempt. The standard
+ authentication package name for NT is called "MSV1.0".
+
+ AuthenticationInformation - Supplies the authentication
+ information specific to the authentication package. It is
+ expected to include identification and authentication
+ information such as user name and password.
+
+ AuthenticationInformationLength - Indicates the length of the
+ authentication information buffer.
+
+ LocalGroups - Optionally supplies a list of additional group
+ identifiers to add to the authenticated user's token. The
+ WORLD group will always be included in the token. A group
+ identifying the logon type (INTERACTIVE, NETWORK, BATCH) will
+ also automatically be included in the token.
+
+ SourceContext - Supplies information identifying the source
+ component (e.g., session manager) and context that may be
+ useful to that component. This information will be included
+ in the token and may later be retrieved.
+
+ ProfileBuffer - Receives a pointer to any returned profile and
+ accounting information about the logged on user's account.
+ This information is authentication package specific and
+ provides such information as the logon shell, home directory
+ and so forth. For an authentication package value of
+ "MSV1.0", a MSV1_0_PROFILE_DATA data structure is returned.
+
+ This buffer is allocated by this service and must be freed
+ using LsaFreeReturnBuffer() when no longer needed.
+
+ ProfileBufferLength - Receives the length (in bytes) of the
+ returned profile buffer.
+
+ LogonId - Points to a buffer which receives a LUID that uniquely
+ identifies this logon session. This LUID was assigned by the
+ domain controller which authenticated the logon information.
+
+ Token - Receives a handle to the new token created for this
+ authentication.
+
+ Quotas - When a primary token is returned, this parameter will be
+ filled in with process quota limits that are to be assigned
+ to the newly logged on user's initial process.
+
+ SubStatus - If the logon failed due to account restrictions, this
+ out parameter will receive an indication as to why the logon
+ failed. This value will only be set to a meaningful value if
+ the user has a legitimate account, but may not currently
+ logon for some reason. The substatus values for
+ authentication package "MSV1.0" are:
+
+ STATUS_INVALID_LOGON_HOURS
+
+ STATUS_INVALID_WORKSTATION
+
+ STATUS_PASSWORD_EXPIRED
+
+ STATUS_ACCOUNT_DISABLED
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - Indicates the caller does not have
+ enough quota to allocate the profile data being returned by
+ the authentication package.
+
+ STATUS_NO_LOGON_SERVERS - Indicates that no domain controllers
+ are currently able to service the authentication request.
+
+ STATUS_LOGON_FAILURE - Indicates the logon attempt failed. No
+ indication as to the reason for failure is given, but typical
+ reasons include mispelled usernames, mispelled passwords.
+
+ STATUS_ACCOUNT_RESTRICTION - Indicates the user account and
+ password were legitimate, but that the user account has some
+ restriction preventing successful logon at this time.
+
+ STATUS_NO_SUCH_PACKAGE - The specified authentication package is
+ unknown to the LSA.
+
+ STATUS_BAD_VALIDATION_CLASS - The authentication information
+ provided is not a validation class known to the specified
+ authentication package.
+
+Routine Description:
+
+ This routine is used to authenticate a user logon attempt. This is
+ used only for user's initial logon, necessary to gain access to NT
+ OS/2. Subsequent (supplementary) authentication requests must be done
+ using LsaCallAuthenticationPackage(). This service will cause a logon
+ session to be created to represent the new logon. It will also return
+ a token representing the newly logged on user.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LSAP_AU_API_MESSAGE Message;
+ PLSAP_LOGON_USER_ARGS Arguments;
+
+
+
+ Arguments = &Message.Arguments.LogonUser;
+
+ //
+ // Set arguments
+ //
+
+ Arguments->AuthenticationPackage = AuthenticationPackage;
+ Arguments->AuthenticationInformation = AuthenticationInformation;
+ Arguments->AuthenticationInformationLength = AuthenticationInformationLength;
+ Arguments->OriginName = (*OriginName);
+ Arguments->LogonType = LogonType;
+ Arguments->SourceContext = (*SourceContext);
+
+ Arguments->LocalGroups = LocalGroups;
+ if ( ARGUMENT_PRESENT(LocalGroups) ) {
+ Arguments->LocalGroupsCount = LocalGroups->GroupCount;
+ } else {
+ Arguments->LocalGroupsCount = 0;
+ }
+
+
+ //
+ // Call the Local Security Authority Server.
+ //
+
+ Message.ApiNumber = LsapAuLogonUserApi;
+ Message.PortMessage.u1.s1.DataLength = sizeof(*Arguments) + 8;
+ Message.PortMessage.u1.s1.TotalLength = sizeof(Message);
+ Message.PortMessage.u2.ZeroInit = 0L;
+
+ Status = ZwRequestWaitReplyPort(
+ LsaHandle,
+ (PPORT_MESSAGE) &Message,
+ (PPORT_MESSAGE) &Message
+ );
+
+ //
+ // We may be returning bogus return values here, but it doesn't
+ // matter. They will just be ignored if an error occured.
+ //
+
+ (*SubStatus) = Arguments->SubStatus;
+ (*ProfileBuffer) = Arguments->ProfileBuffer;
+ (*ProfileBufferLength) = Arguments->ProfileBufferLength;
+ (*LogonId) = Arguments->LogonId;
+ (*Token) = Arguments->Token;
+ (*Quotas) = Arguments->Quotas;
+
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = Message.ReturnedStatus;
+ if ( !NT_SUCCESS(Status) ) {
+ //DbgPrint("LSA AU: Logon User Failed %lx\n",Status);
+ ;
+ }
+ } else {
+#if DBG
+ DbgPrint("LSA AU: Logon User NtRequestWaitReply Failed %lx\n",Status);
+#else
+ ;
+#endif
+ }
+
+
+
+ return Status;
+
+
+}
+
+
+NTSTATUS
+LsaCallAuthenticationPackage (
+ IN HANDLE LsaHandle,
+ IN ULONG AuthenticationPackage,
+ IN PVOID ProtocolSubmitBuffer,
+ IN ULONG SubmitBufferLength,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferLength,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Arguments:
+
+ LsaHandle - Supplies a handle obtained in a previous call to
+ LsaRegisterLogonProcess.
+
+ AuthenticationPackage - Supplies the ID of the authentication
+ package to use for the logon attempt. The standard
+ authentication package name for NT is called "MSV1.0".
+
+ ProtocolSubmitBuffer - Supplies a protocol message specific to
+ the authentication package.
+
+ SubmitBufferLength - Indicates the length of the submitted
+ protocol message buffer.
+
+ ProtocolReturnBuffer - Receives a pointer to a returned protocol
+ message whose format and semantics are specific to the
+ authentication package.
+
+ This buffer is allocated by this service and must be freed
+ using LsaFreeReturnBuffer() when no longer needed.
+
+ ReturnBufferLength - Receives the length (in bytes) of the
+ returned profile buffer.
+
+ ProtocolStatus - Assuming the services completion is
+ STATUS_SUCCESS, this parameter will receive completion status
+ returned by the specified authentication package. The list
+ of status values that may be returned are authentication
+ package specific.
+
+Return Status:
+
+ STATUS_SUCCESS - The call was made to the authentication package.
+ The ProtocolStatus parameter must be checked to see what the
+ completion status from the authentication package is.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the call could
+ not be completed because the client does not have sufficient
+ quota to allocate the return buffer.
+
+ STATUS_NO_SUCH_PACKAGE - The specified authentication package is
+ unknown to the LSA.
+
+Routine Description:
+
+ This routine is used when a logon process needs to communicate with an
+ authentication package. There are several reasons why a logon process
+ may want to do this. Some examples are:
+
+ o To implement multi-message authentication protocols (such as
+ the LAN Manager Challenge-response protocol.
+
+ o To notify the authentication package of interesting state
+ change information, such as LAN Manager notifying the MSV1.0
+ package that a previously unreachable domain controller is
+ now reachable. In this example, the authentication package
+ would re-logon any users logged on to that domain controller.
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LSAP_AU_API_MESSAGE Message;
+ PLSAP_CALL_PACKAGE_ARGS Arguments;
+
+
+
+ Arguments = &Message.Arguments.CallPackage;
+
+ //
+ // Set arguments
+ //
+
+ Arguments->AuthenticationPackage = AuthenticationPackage;
+ Arguments->ProtocolSubmitBuffer = ProtocolSubmitBuffer;
+ Arguments->SubmitBufferLength = SubmitBufferLength;
+
+
+
+ //
+ // Call the Local Security Authority Server.
+ //
+
+ Message.ApiNumber = LsapAuCallPackageApi;
+ Message.PortMessage.u1.s1.DataLength = sizeof(*Arguments) + 8;
+ Message.PortMessage.u1.s1.TotalLength = sizeof(Message);
+ Message.PortMessage.u2.ZeroInit = 0L;
+
+ Status = ZwRequestWaitReplyPort(
+ LsaHandle,
+ (PPORT_MESSAGE) &Message,
+ (PPORT_MESSAGE) &Message
+ );
+
+ //
+ // We may be returning bogus return values here, but it doesn't
+ // matter. They will just be ignored if an error occured.
+ //
+
+ (*ProtocolReturnBuffer) = Arguments->ProtocolReturnBuffer;
+ (*ReturnBufferLength) = Arguments->ReturnBufferLength;
+ (*ProtocolStatus) = Arguments->ProtocolStatus;
+
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = Message.ReturnedStatus;
+#if DBG
+ if ( !NT_SUCCESS(Status) ) {
+ DbgPrint("LSA AU: Call Package Failed %lx\n",Status);
+ }
+ } else {
+ DbgPrint("LSA AU: Call Package Failed %lx\n",Status);
+#endif //DBG
+ }
+
+
+
+ return Status;
+
+}
+
+
+NTSTATUS
+LsaDeregisterLogonProcess (
+ IN HANDLE LsaHandle
+ )
+
+/*++
+
+ This function deletes the caller's logon process context.
+
+
+ --- WARNING ---
+
+ Logon Processes are part of the Trusted Computer Base, and,
+ as such, are expected to be debugged to a high degree. If
+ a logon process deregisters, we will believe it. This
+ allows us to re-use the old Logon Process context value.
+ If the Logon process accidently uses its context value
+ after freeing it, strange things may happen. LIkewise,
+ if a client calls to release a context that has already
+ been released, then LSA may grind to a halt.
+
+
+
+Arguments:
+
+ LsaHandle - Supplies a handle obtained in a previous call to
+ LsaRegisterLogonProcess.
+
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LSAP_AU_API_MESSAGE Message;
+
+
+ //
+ // Call the Local Security Authority Server.
+ //
+
+ Message.ApiNumber = LsapAuDeregisterLogonProcessApi;
+ Message.PortMessage.u1.s1.DataLength = 8;
+ Message.PortMessage.u1.s1.TotalLength = sizeof(Message);
+ Message.PortMessage.u2.ZeroInit = 0L;
+
+ Status = ZwRequestWaitReplyPort(
+ LsaHandle,
+ (PPORT_MESSAGE) &Message,
+ (PPORT_MESSAGE) &Message
+ );
+
+
+
+ if ( NT_SUCCESS(Status) ) {
+ Status = Message.ReturnedStatus;
+ if ( NT_SUCCESS(Status) ) {
+
+ NTSTATUS TempStatus;
+ TempStatus = ZwClose(LsaHandle);
+ ASSERT(NT_SUCCESS(TempStatus));
+ }
+ else {
+ DbgPrint("LSA AU: DeRregisterLogonProcess Failed 0x%lx\n",Status);
+ }
+ } else {
+ DbgPrint("LSA AU: Package Lookup NtRequestWaitReply Failed 0x%lx\n",Status);
+ }
+
+ return Status;
+}
diff --git a/private/lsa/client/autest1.c b/private/lsa/client/autest1.c
new file mode 100644
index 000000000..67f956231
--- /dev/null
+++ b/private/lsa/client/autest1.c
@@ -0,0 +1,165 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ autest1.c
+
+Abstract:
+
+ This module performs a set of authentication package/logon
+ process testing.
+
+
+ TO BUILD:
+
+ nmake UMTYPE=console UMTEST=autest1
+
+
+Author:
+
+ Jim Kelly 3-Apr-1992.
+
+Revision History:
+
+--*/
+
+
+
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Locally needed data types //
+// //
+///////////////////////////////////////////////////////////////////////
+
+
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Local Macros //
+// //
+///////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Global variables //
+// //
+///////////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Internal routine definitions
+// //
+///////////////////////////////////////////////////////////////////////
+
+VOID
+main (
+ IN int c,
+ IN PCHAR v[]
+ );
+
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+///////////////////////////////////////////////////////////////////////
+
+
+VOID
+main (
+ IN int c,
+ IN PCHAR v[]
+ )
+/*++
+
+
+Routine Description:
+
+ This routine is the main entry routine for this test.
+
+Arguments:
+
+ TBS
+
+Return Value:
+
+ TBS
+
+--*/
+{
+ NTSTATUS Status;
+ STRING LPName;
+ HANDLE LsaHandle;
+ LSA_OPERATIONAL_MODE SecurityMode;
+
+ RtlInitString( &LPName, "Test");
+
+
+ DbgPrint("Temporary Restriction: THIS TEST MUST BE RUN WITH TCB ENABLED\n\n");
+
+
+
+
+
+
+ //
+ // register as a logon process
+ //
+
+ DbgPrint("Registering as logon process . . . . . . . . .");
+ Status = LsaRegisterLogonProcess( &LPName, &LsaHandle, &SecurityMode );
+ if (NT_SUCCESS(Status)) {
+
+ DbgPrint("Succeeded\n");
+
+
+
+ //
+ // de-register as a logon process
+ //
+
+ DbgPrint("Deregistering as logon process . . . . . . . .");
+ Status = LsaDeregisterLogonProcess( LsaHandle );
+ if (NT_SUCCESS(Status)) {
+
+ DbgPrint("Succeeded\n");
+ } else {
+ DbgPrint("*** FAILED ***\n");
+ }
+
+ } else {
+ DbgPrint("*** FAILED ***\n");
+ }
+
+ DBG_UNREFERENCED_PARAMETER(c);
+ DBG_UNREFERENCED_PARAMETER(v);
+
+}
diff --git a/private/lsa/client/dirs b/private/lsa/client/dirs
new file mode 100644
index 000000000..225ce78c4
--- /dev/null
+++ b/private/lsa/client/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=kernel \
+ user \
+ ssp
diff --git a/private/lsa/client/kernel/makefile b/private/lsa/client/kernel/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/client/kernel/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/client/kernel/sources b/private/lsa/client/kernel/sources
new file mode 100644
index 000000000..36d5319a5
--- /dev/null
+++ b/private/lsa/client/kernel/sources
@@ -0,0 +1,36 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=client
+
+TARGETNAME=lsakrnlp
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\inc
+
+SOURCES= austub.c
+
+C_DEFINES = -D_NTSYSTEM_=1
diff --git a/private/lsa/client/lsadllp.h b/private/lsa/client/lsadllp.h
new file mode 100644
index 000000000..51381f867
--- /dev/null
+++ b/private/lsa/client/lsadllp.h
@@ -0,0 +1,29 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ lsadllp.h
+
+Abstract:
+
+ Local Security Authority DLL include file.
+
+Author:
+
+ Jim Kelly (JimK) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _LSADLLP_
+#define _LSADLLP_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <ntlsa.h>
+#include "aup.h" // Common AUTHENTICATION related definitions
+
+#endif // _LSADLLP_
diff --git a/private/lsa/client/ssp/connmgr.c b/private/lsa/client/ssp/connmgr.c
new file mode 100644
index 000000000..f0078adaa
--- /dev/null
+++ b/private/lsa/client/ssp/connmgr.c
@@ -0,0 +1,271 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1991 - 1992
+//
+// File: connmgr.c
+//
+// Contents: Connection Manager code for KSecDD
+//
+//
+// History: 3 Jun 92 RichardW Created
+//
+//------------------------------------------------------------------------
+
+#include <sspdrv.h>
+
+ULONG cClients = 0; // Connection count
+KSPIN_LOCK ConnectSpinLock; // Spin Lock guard for connection list
+PClient pClientList; // List of clients
+PSTR LogonProcessString = "KSecDD";
+ULONG PackageId;
+BOOLEAN
+InitConnMgr(void);
+
+#pragma alloc_text(INIT, InitConnMgr)
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitConnMgr
+//
+// Synopsis: Initializes all this stuff
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+BOOLEAN
+InitConnMgr(void)
+{
+ if (!KsecInitMemory())
+ {
+ return(FALSE);
+ }
+
+ if (!NT_SUCCESS(InitializePackages()))
+ {
+ return(FALSE);
+ }
+
+ KeInitializeSpinLock(&ConnectSpinLock);
+ pClientList = NULL;
+ fInitialized = TRUE;
+
+ return(TRUE);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: CreateClient
+//
+// Synopsis: Creates a client representing this caller. Establishes
+// a connection with the SPM.
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+NTSTATUS
+CreateClient(PClient * ppClient)
+{
+ PClient pClient;
+ NTSTATUS scRet;
+ KIRQL OldIrql;
+ HANDLE hEvent;
+ STRING LogonProcessName;
+ STRING PackageName;
+ LSA_OPERATIONAL_MODE LsaMode;
+ HANDLE LsaHandle = NULL;
+
+
+
+
+ if (!fInitialized)
+ {
+ if (!InitConnMgr())
+ {
+ DebugLog((DEB_ERROR,"InitConnMgr Failed!\n"));
+ return(SEC_E_INTERNAL_ERROR);
+ }
+ }
+ //
+ // Call the LSA to register this logon process.
+ //
+
+ RtlInitString(
+ &LogonProcessName,
+ LogonProcessString
+ );
+
+
+ scRet = LsaRegisterLogonProcess(
+ &LogonProcessName,
+ &LsaHandle,
+ &LsaMode
+ );
+
+ if (!NT_SUCCESS(scRet))
+ {
+ DebugLog((DEB_ERROR,"KSec: Connection failed, postponing\n"));
+ return(SEC_E_INTERNAL_ERROR);
+ }
+
+ //
+ // Lookup the authentication package.
+ //
+
+ RtlInitString(
+ &PackageName,
+ MSV1_0_PACKAGE_NAME
+ );
+
+ scRet = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &PackageName,
+ &PackageId
+ );
+ if (!NT_SUCCESS(scRet))
+ {
+ NtClose(LsaHandle);
+ return(SEC_E_INTERNAL_ERROR);
+ }
+
+ pClient = (PClient) ExAllocatePool(NonPagedPool, sizeof(Client));
+
+ if (!pClient)
+ {
+ DebugLog((DEB_ERROR,"KSec: ExAllocatePool returned NULL.\n"));
+ NtClose(LsaHandle);
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ pClient->ProcessId = PsGetCurrentProcess();
+ pClient->fClient = 0;
+ pClient->hPort = LsaHandle;
+ pClient->cRefs = 1;
+
+
+
+ // Adding the connection in to the client list:
+ //
+ // This has got to be MT/MP safe, so first,
+
+ // get exclusive access to the Connection list:
+
+ KeAcquireSpinLock(&ConnectSpinLock, &OldIrql);
+
+ // Now, add the entry:
+
+ cClients++;
+ pClient->pNext = pClientList;
+ pClientList = pClient;
+
+ // Now, free the spin lock:
+
+ KeReleaseSpinLock(&ConnectSpinLock, OldIrql);
+
+ *ppClient = pClient;
+
+ return(STATUS_SUCCESS);
+
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: LocateClient
+//
+// Synopsis: Locates a client record based on current process Id
+//
+// Effects: Grabs ConnectSpinLock for duration of search.
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+NTSTATUS
+LocateClient( PClient * ppClient)
+{
+ PClient pClient;
+ KIRQL OldIrql;
+ PEPROCESS pCurrent;
+
+ KeAcquireSpinLock(&ConnectSpinLock, &OldIrql);
+
+ pClient = pClientList;
+ pCurrent = PsGetCurrentProcess();
+
+ while (pClient)
+ {
+ if (pClient->ProcessId == pCurrent)
+ {
+ *ppClient = pClient;
+ pClient->cRefs++;
+ break;
+ }
+ pClient = pClient->pNext;
+ }
+
+ KeReleaseSpinLock(&ConnectSpinLock, OldIrql);
+
+ if (pClient == NULL)
+ return(STATUS_OBJECT_NAME_NOT_FOUND);
+ else
+ return(STATUS_SUCCESS);
+
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: FreeClient
+//
+// Synopsis: Frees access to a client record
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+void
+FreeClient( PClient pClient)
+{
+ if (ExInterlockedDecrementLong(&pClient->cRefs, &ConnectSpinLock) == ResultNegative)
+ {
+ DebugLog((DEB_ERROR,"KSec: Client record ref count went negative\n"));
+ DebugLog((DEB_ERROR,"KSec: ---Resetting to 0\n"));
+ pClient->cRefs = 0;
+ }
+}
+
+
+
+
+
+
diff --git a/private/lsa/client/ssp/connmgr.h b/private/lsa/client/ssp/connmgr.h
new file mode 100644
index 000000000..0ac6aa21d
--- /dev/null
+++ b/private/lsa/client/ssp/connmgr.h
@@ -0,0 +1,67 @@
+
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1991 - 1992
+//
+// File: connmgr.h
+//
+// Contents: Connection Manager code for KSecDD
+//
+//
+// History: 3 Jun 92 RichardW Created
+//
+//------------------------------------------------------------------------
+
+#ifndef __CONNMGR_H__
+#define __CONNMGR_H__
+
+
+typedef struct _Client {
+ struct _Client * pNext;
+ PVOID ProcessId;
+ HANDLE hPort;
+ ULONG fClient;
+ LONG cRefs;
+} Client, *PClient;
+
+
+
+typedef struct _KernelContext {
+ struct _KernelContext * pNext; // Link to next context
+ struct _KernelContext * pPrev; // Link to previous context
+ UCHAR UserSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
+ UCHAR LanmanSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
+ HANDLE TokenHandle;
+ PACCESS_TOKEN AccessToken;
+} KernelContext, *PKernelContext;
+
+
+
+
+// FSP connections are managed thusly:
+//
+// A Client structure is allocated for each FSP that connects. At the same
+// time, the LPC port is created Additionally, hanging off a Client is the
+// list of active impersonations, and the list of contexts.
+
+
+#define CLIENT_CONTEXT 2 // Client has at least one context
+
+#define CONNFLAG_BROKEN 2 // Connection is broken and cannot be reused
+#define CONNFLAG_CONTEXT 4 // Connection has contexts
+#define CONNFLAG_IMPERSON 8 // Connection has impersonations
+
+
+BOOLEAN InitConnMgr(void);
+NTSTATUS CreateClient(PClient *);
+NTSTATUS LocateClient(PClient *);
+void FreeClient(PClient);
+NTSTATUS CreateConnection(HANDLE *);
+void AddKernelContext(PKernelContext *, PKSPIN_LOCK, PKernelContext);
+SECURITY_STATUS DeleteKernelContext(PKernelContext *, PKSPIN_LOCK, PKernelContext);
+
+extern ULONG PackageId;
+
+#endif // __CONNMGR_H__
diff --git a/private/lsa/client/ssp/context.c b/private/lsa/client/ssp/context.c
new file mode 100644
index 000000000..9fcbc8600
--- /dev/null
+++ b/private/lsa/client/ssp/context.c
@@ -0,0 +1,146 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: context.cxx
+//
+// Contents: context kernel-mode functions
+//
+//
+// History: 3/17/94 MikeSw Created
+//
+//------------------------------------------------------------------------
+#include <sspdrv.h>
+
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: DeleteKernelContext
+//
+// Synopsis: Deletes a kernel context
+//
+// Effects: Frees memory, closes token handle.
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS
+DeleteKernelContext(PKernelContext * ppList,
+ PKSPIN_LOCK pslLock,
+ PKernelContext pContext)
+{
+ KIRQL OldIrql;
+
+ //
+ // First, find the record, then unlink the record from the list,
+ // and fix up pointers.
+ //
+
+ KeAcquireSpinLock(pslLock, &OldIrql);
+
+
+ if (!pContext)
+ {
+ KeReleaseSpinLock(pslLock, OldIrql);
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ //
+ // Now unlink from the list
+ //
+
+ if (pContext->pPrev)
+ {
+ pContext->pPrev->pNext = pContext->pNext;
+ }
+ else
+ {
+ *ppList = pContext->pNext;
+ }
+
+
+ if (pContext->pNext)
+ {
+ pContext->pNext->pPrev = pContext->pPrev;
+ }
+
+ //
+ // copy out the package-specific context to return.
+ // We are done with the list so we can release the spin lock
+ //
+
+ KeReleaseSpinLock(pslLock, OldIrql);
+
+
+ if (pContext->TokenHandle != NULL)
+ {
+ NtClose(pContext->TokenHandle);
+ }
+ if (pContext->AccessToken != NULL)
+ {
+ ObDereferenceObject(pContext->AccessToken);
+ }
+
+ // And, finally, return the context record to our pool:
+
+ FreeContextRec(pContext);
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: AddKernelContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+void
+AddKernelContext( PKernelContext * ppList,
+ PKSPIN_LOCK pslLock,
+ PKernelContext pContext)
+{
+ KIRQL OldIrql;
+
+
+ KeAcquireSpinLock(pslLock, &OldIrql);
+
+ pContext->pNext = *ppList;
+ if (pContext->pNext)
+ {
+ pContext->pNext->pPrev = pContext;
+ }
+ pContext->pPrev = NULL;
+
+ *ppList = pContext;
+
+ KeReleaseSpinLock(pslLock, OldIrql);
+
+}
+
+
+
+
diff --git a/private/lsa/client/ssp/ksecdd.c b/private/lsa/client/ssp/ksecdd.c
new file mode 100644
index 000000000..e364de1fb
--- /dev/null
+++ b/private/lsa/client/ssp/ksecdd.c
@@ -0,0 +1,440 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1991 - 1992
+//
+// File: KSecDD.C
+//
+// Contents: Base level stuff for the device driver
+//
+//
+// History: 19 May 92, RichardW Blatently stolen from DarrylH
+//
+//------------------------------------------------------------------------
+#include <sspdrv.h>
+
+
+//
+// Define the local routines used by this driver module.
+//
+
+static
+NTSTATUS
+KsecDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+static
+NTSTATUS
+KsecQueryFileInformation(
+ OUT PVOID Buffer,
+ IN OUT PULONG Length,
+ IN FILE_INFORMATION_CLASS InformationClass
+ );
+
+static
+NTSTATUS
+KsecQueryVolumeInformation(
+ OUT PVOID Buffer,
+ IN OUT PULONG Length,
+ IN FS_INFORMATION_CLASS InformationClass
+ );
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, KsecQueryVolumeInformation)
+#pragma alloc_text(PAGE, KsecDispatch)
+#pragma alloc_text(PAGE, KsecQueryFileInformation)
+
+#if DBG
+ULONG KsecInfoLevel;
+
+void
+KsecDebugOut(unsigned long Mask,
+ const char * Format,
+ ...)
+{
+ PETHREAD pThread;
+ PEPROCESS pProcess;
+ va_list ArgList;
+ char szOutString[256];
+
+ if (KsecInfoLevel & Mask)
+ {
+ pThread = PsGetCurrentThread();
+ pProcess = PsGetCurrentProcess();
+
+ va_start(ArgList, Format);
+ DbgPrint("%#x.%#x> KSec: ", pProcess, pThread);
+ if (_vsnprintf(szOutString, sizeof(szOutString),Format, ArgList) < 0)
+ {
+ //
+ // Less than zero indicates that the string could not be
+ // fitted into the buffer. Output a special message indicating
+ // that:
+ //
+
+ DbgPrint("Error printing message\n");
+
+ }
+ else
+ {
+ DbgPrint(szOutString);
+ }
+ }
+}
+#endif
+
+
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+
+/*++
+
+Routine Description:
+
+ This is the initialization routine for the synchronous NULL device driver.
+ This routine creates the device object for the NullS device and performs
+ all other driver initialization.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by the system.
+
+Return Value:
+
+ The function value is the final status from the initialization operation.
+
+--*/
+
+{
+ UNICODE_STRING nameString;
+ PDEVICE_OBJECT deviceObject;
+ NTSTATUS status;
+
+ //
+ // Create the device object.
+ //
+
+ RtlInitUnicodeString( &nameString, L"\\Device\\KSecDD" );
+ status = IoCreateDevice( DriverObject,
+ 0L,
+ &nameString,
+ FILE_DEVICE_NULL,
+ 0,
+ FALSE,
+ &deviceObject );
+ if (!NT_SUCCESS( status )) {
+ return status;
+ }
+
+ //
+ // Setting the following flag changes the timing of how many I/O's per
+ // second can be accomplished by going through the NULL device driver
+ // from being simply getting in and out of the driver, to getting in and
+ // out with the overhead of building an MDL, probing and locking buffers,
+ // unlocking the pages, and deallocating the MDL. This flag should only
+ // be set for performance testing.
+ //
+
+// deviceObject->Flags |= DO_DIRECT_IO;
+
+ //
+ // Initialize the driver object with this device driver's entry points.
+ //
+
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = KsecDispatch;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = KsecDispatch;
+ DriverObject->MajorFunction[IRP_MJ_READ] = KsecDispatch;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = KsecDispatch;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = KsecDispatch;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = KsecDispatch;
+
+
+
+ if (!InitConnMgr())
+ {
+ DebugLog((DEB_ERROR,"Failed to initialize\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ DebugLog((DEB_TRACE,"Security device driver loaded\n"));
+ return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+KsecDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the main dispatch routine for the synchronous NULL device
+ driver. It accepts an I/O Request Packet, performs the request, and then
+ returns with the appropriate status.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this driver.
+
+ Irp - Pointer to the request packet representing the I/O request.
+
+Return Value:
+
+ The function value is the status of the operation.
+
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpSp;
+// KIRQL oldIrql;
+ PVOID buffer;
+ ULONG length;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ //
+ // Get a pointer to the current stack location in the IRP. This is where
+ // the function codes and parameters are stored.
+ //
+
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Case on the function that is being performed by the requestor. If the
+ // operation is a valid one for this device, then make it look like it was
+ // successfully completed, where possible.
+ //
+
+ switch (irpSp->MajorFunction) {
+
+ //
+ // For both create/open and close operations, simply set the information
+ // field of the I/O status block and complete the request.
+ //
+
+ case IRP_MJ_CREATE:
+ case IRP_MJ_CLOSE:
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0L;
+ break;
+
+ //
+ // For read operations, set the information field of the I/O status
+ // block, set an end-of-file status, and complete the request.
+ //
+
+ case IRP_MJ_READ:
+ Irp->IoStatus.Status = STATUS_END_OF_FILE;
+ Irp->IoStatus.Information = 0L;
+ break;
+
+ //
+ // For write operations, set the information field of the I/O status
+ // block to the number of bytes which were supposed to have been written
+ // to the file and complete the request.
+ //
+
+ case IRP_MJ_WRITE:
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = irpSp->Parameters.Write.Length;
+ break;
+
+ case IRP_MJ_QUERY_INFORMATION:
+ buffer = Irp->AssociatedIrp.SystemBuffer;
+ length = irpSp->Parameters.QueryFile.Length;
+ Irp->IoStatus.Status = KsecQueryFileInformation( buffer,
+ &length,
+ irpSp->Parameters.QueryFile.FileInformationClass );
+ Irp->IoStatus.Information = length;
+ break;
+
+ case IRP_MJ_QUERY_VOLUME_INFORMATION:
+ buffer = Irp->AssociatedIrp.SystemBuffer;
+ length = irpSp->Parameters.QueryVolume.Length;
+ Irp->IoStatus.Status = KsecQueryVolumeInformation( buffer,
+ &length,
+ irpSp->Parameters.QueryVolume.FsInformationClass );
+ Irp->IoStatus.Information = length;
+ break;
+ }
+
+ //
+ // Copy the final status into the return status, complete the request and
+ // get out of here.
+ //
+
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest( Irp, 0 );
+ return status;
+}
+
+static
+NTSTATUS
+KsecQueryFileInformation(
+ OUT PVOID Buffer,
+ IN PULONG Length,
+ IN FILE_INFORMATION_CLASS InformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This routine queries information about the opened file and returns the
+ information in the specified buffer provided that the buffer is large
+ enough and the specified type of information about the file is supported
+ by this device driver.
+
+ Information about files supported by this driver are:
+
+ o FileStandardInformation
+
+Arguments:
+
+ Buffer - Supplies a pointer to the buffer in which to return the
+ information.
+
+ Length - Supplies the length of the buffer on input and the length of
+ the data actually written on output.
+
+ InformationClass - Supplies the information class that is being queried.
+
+Return Value:
+
+ The function value is the final status of the query operation.
+
+--*/
+
+{
+ PFILE_STANDARD_INFORMATION standardBuffer;
+
+ PAGED_CODE();
+ //
+ // Switch on the type of information that the caller would like to query
+ // about the file.
+ //
+
+ switch (InformationClass) {
+
+ case FileStandardInformation:
+
+ //
+ // Return the standard information about the file.
+ //
+
+ standardBuffer = (PFILE_STANDARD_INFORMATION) Buffer;
+ *Length = (ULONG) sizeof( FILE_STANDARD_INFORMATION );
+ standardBuffer->NumberOfLinks = 1;
+ standardBuffer->DeletePending = FALSE;
+ standardBuffer->AllocationSize.LowPart = 0;
+ standardBuffer->AllocationSize.HighPart = 0;
+ standardBuffer->Directory = FALSE;
+ standardBuffer->EndOfFile.LowPart = 0;
+ standardBuffer->EndOfFile.HighPart = 0;
+ break;
+
+ default:
+
+ //
+ // An invalid (or unsupported) information class has been queried
+ // for the file. Return the appropriate status.
+ //
+
+ return STATUS_INVALID_INFO_CLASS;
+
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+KsecQueryVolumeInformation(
+ OUT PVOID Buffer,
+ IN PULONG Length,
+ IN FS_INFORMATION_CLASS InformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This routine queries information about the opened volume and returns the
+ information in the specified buffer.
+
+ Information about volumes supported by this driver are:
+
+ o FileFsDeviceInformation
+
+Arguments:
+
+ Buffer - Supplies a pointer to the buffer in which to return the
+ information.
+
+ Length - Supplies the length of the buffer on input and the length of
+ the data actually written on output.
+
+ InformationClass - Supplies the information class that is being queried.
+
+Return Value:
+
+ The function value is the final status of the query operation.
+
+--*/
+
+{
+ PFILE_FS_DEVICE_INFORMATION deviceBuffer;
+
+
+ PAGED_CODE();
+ //
+ // Switch on the type of information that the caller would like to query
+ // about the volume.
+ //
+
+ switch (InformationClass) {
+
+ case FileFsDeviceInformation:
+
+ //
+ // Return the device information about the volume.
+ //
+
+ deviceBuffer = (PFILE_FS_DEVICE_INFORMATION) Buffer;
+ *Length = sizeof( FILE_FS_DEVICE_INFORMATION );
+ deviceBuffer->DeviceType = FILE_DEVICE_NULL;
+ break;
+
+ default:
+
+ //
+ // An invalid (or unsupported) information class has been queried
+ // for the volume. Return the appropriate status.
+ //
+
+ return STATUS_INVALID_INFO_CLASS;
+
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/lsa/client/ssp/ksecdd.def b/private/lsa/client/ssp/ksecdd.def
new file mode 100644
index 000000000..872b82735
--- /dev/null
+++ b/private/lsa/client/ssp/ksecdd.def
@@ -0,0 +1,25 @@
+LIBRARY ksecdd.sys
+
+DESCRIPTION 'Kernel Security Interface'
+
+EXPORTS
+ InitSecurityInterfaceW
+ AcquireCredentialsHandleW
+ FreeCredentialsHandle
+ InitializeSecurityContextW
+ AcceptSecurityContext
+ ImpersonateSecurityContext
+ RevertSecurityContext
+ DeleteSecurityContext
+ ApplyControlToken
+ QueryContextAttributesW
+ FreeContextBuffer
+ MakeSignature
+ VerifySignature
+ SealMessage
+ UnsealMessage
+ MapSecurityError
+ EnumerateSecurityPackagesW
+ QuerySecurityContextToken
+ GetSecurityUserInfo
+
diff --git a/private/lsa/client/ssp/ksecdd.h b/private/lsa/client/ssp/ksecdd.h
new file mode 100644
index 000000000..9e1bce300
--- /dev/null
+++ b/private/lsa/client/ssp/ksecdd.h
@@ -0,0 +1,102 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1991 - 1992
+//
+// File: KSECDD.H
+//
+// Contents: Structures and defines for the security device driver
+//
+//
+// History: 19 May 92, RichardW Created
+//
+//------------------------------------------------------------------------
+
+#ifndef __KSECDD_H__
+#define __KSECDD_H__
+
+#include <ntos.h>
+
+#include <spseal.h> // prototypes for seal & unseal
+
+
+
+SECURITY_STATUS SEC_ENTRY
+DeleteUserModeContext(
+ PCtxtHandle phContext // Contxt to delete
+ );
+
+SECURITY_STATUS SEC_ENTRY
+InitUserModeContext(
+ PCtxtHandle phContext, // Contxt to init
+ PSecBuffer pContextBuffer
+ );
+
+
+SECURITY_STATUS
+InitializePackages(void);
+
+
+VOID * SEC_ENTRY
+SecAllocate(ULONG cbMemory);
+
+void SEC_ENTRY
+SecFree(PVOID pvMemory);
+
+SECURITY_STATUS SEC_ENTRY
+IsOkayToExec(PClient * ppClient);
+
+BOOLEAN
+GetTokenBuffer(
+ IN PSecBufferDesc TokenDescriptor OPTIONAL,
+ IN ULONG BufferIndex,
+ OUT PVOID * TokenBuffer,
+ OUT PULONG TokenSize,
+ IN BOOLEAN ReadonlyOK
+ );
+
+BOOLEAN
+GetSecurityToken(
+ IN PSecBufferDesc TokenDescriptor OPTIONAL,
+ IN ULONG BufferIndex,
+ OUT PSecBuffer * TokenBuffer
+ );
+
+
+
+// Global Variables:
+
+extern KSPIN_LOCK ConnectSpinLock;
+extern BOOLEAN fInitialized;
+
+#define DEB_ERROR 0x1
+#define DEB_WARN 0x2
+#define DEB_TRACE 0x4
+
+
+
+
+#ifdef POOL_TAGGING
+#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a, b, 'cesK')
+#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a, b, 'cesK')
+#endif
+
+
+#if DBG
+void
+KsecDebugOut(unsigned long Mask,
+ const char * Format,
+ ...);
+
+#define DebugStmt(x) x
+#define DebugLog(x) KsecDebugOut x
+#else
+#define DebugStmt(x)
+#define DebugLog(x)
+#endif
+
+
+
+
+#endif // __KSECDD_H__
diff --git a/private/lsa/client/ssp/makefile b/private/lsa/client/ssp/makefile
new file mode 100644
index 000000000..124a39ca1
--- /dev/null
+++ b/private/lsa/client/ssp/makefile
@@ -0,0 +1,28 @@
+############################################################################
+#
+# Copyright (C) 1992, Microsoft Corporation.
+#
+# All rights reserved.
+#
+############################################################################
+
+!if "$(NTMAKEENV)" != ""
+
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
+
+!else
+
+
+default: all
+
+!include filelist.mk
+!include $(COMMON)\src\win40.mk
+!include depend.mk
+
+
+!endif
diff --git a/private/lsa/client/ssp/memmgr.c b/private/lsa/client/ssp/memmgr.c
new file mode 100644
index 000000000..c46ec3cbc
--- /dev/null
+++ b/private/lsa/client/ssp/memmgr.c
@@ -0,0 +1,192 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1991 - 1992
+//
+// File: memmgr.c
+//
+// Contents: Fast memory manager code for KSecDD
+//
+//
+// History: 23 Feb 93 RichardW Created
+//
+//------------------------------------------------------------------------
+
+#include <sspdrv.h>
+
+
+KSPIN_LOCK ResourceSpinLock; // Spin Lock guard for resource lists
+PKernelContext pFreeContexts; // Free context records
+ULONG cFreeContexts; // Count of free context records
+
+#define MAX_FREE_CONTEXTS 20 // max number of entries on the free
+ // context list.
+
+// Debugging statistics.
+//
+// These track the number of various records in use
+// or allocated from the system.
+
+
+#if DBG
+ULONG cTotalCtxtRecs;
+ULONG cActiveCtxtRecs;
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, KsecInitMemory)
+#endif
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: KsecInitMemory
+//
+// Synopsis: Initializes free lists and spin lock
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+BOOLEAN
+KsecInitMemory(void)
+{
+
+ KeInitializeSpinLock(&ResourceSpinLock);
+
+ pFreeContexts = NULL;
+
+
+#if DBG
+ cActiveCtxtRecs = 0;
+ cTotalCtxtRecs = 0;
+#endif
+
+ return(TRUE);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: AllocContextRec
+//
+// Synopsis: Allocates a KernelContext structure
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+PKernelContext
+AllocContextRec(void)
+{
+ PKernelContext pContext = NULL;
+ KIRQL OldIrql;
+
+ KeAcquireSpinLock(&ResourceSpinLock, &OldIrql);
+
+ if (pFreeContexts)
+ {
+ pContext = pFreeContexts;
+ pFreeContexts = pFreeContexts->pNext;
+ ASSERT(cFreeContexts != 0);
+ cFreeContexts--;
+ }
+
+ KeReleaseSpinLock(&ResourceSpinLock, OldIrql);
+
+ if (pContext == NULL)
+ {
+ pContext = (PKernelContext)
+ ExAllocatePool(NonPagedPool, sizeof(KernelContext));
+ DebugStmt(cTotalCtxtRecs++);
+ }
+
+ if (pContext == NULL)
+ {
+ DebugLog((DEB_ERROR,"Could not allocate from pool!\n"));
+ return(NULL);
+ }
+
+ pContext->pNext = NULL;
+ pContext->pPrev = NULL;
+
+ DebugStmt(cActiveCtxtRecs++);
+
+ return(pContext);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: FreeContextRec
+//
+// Synopsis: Returns a KernelContext record to the free list
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+void
+FreeContextRec(PKernelContext pContext)
+{
+ KIRQL OldIrql;
+
+ KeAcquireSpinLock(&ResourceSpinLock, &OldIrql);
+
+ //
+ // If we haven't reached the threshold for our free list, return
+ // this to the free list.
+ //
+
+ if (cFreeContexts < MAX_FREE_CONTEXTS)
+ {
+ pContext->pNext = pFreeContexts;
+
+ pFreeContexts = pContext;
+ cFreeContexts++;
+ KeReleaseSpinLock(&ResourceSpinLock, OldIrql);
+
+ }
+ else
+ {
+ //
+ // Release our lock, to avoid deadlocks, and
+ // just return the context to the pool.
+ //
+
+ KeReleaseSpinLock(&ResourceSpinLock, OldIrql);
+ ExFreePool(pContext);
+ }
+
+ DebugStmt(cActiveCtxtRecs--);
+
+
+}
+
+
+
+
+
diff --git a/private/lsa/client/ssp/memmgr.h b/private/lsa/client/ssp/memmgr.h
new file mode 100644
index 000000000..39b803458
--- /dev/null
+++ b/private/lsa/client/ssp/memmgr.h
@@ -0,0 +1,30 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1991 - 1992
+//
+// File: memmgr.h
+//
+// Contents: Memory Manager code for KSecDD
+//
+//
+// History: 23 Feb 93 RichardW Created
+//
+//------------------------------------------------------------------------
+
+#ifndef __MEMMGR_H__
+#define __MEMMGR_H__
+
+
+
+PKernelContext AllocContextRec(void);
+void FreeContextRec(PKernelContext);
+
+
+BOOLEAN KsecInitMemory(void);
+
+// Global Resource Spin lock
+extern KSPIN_LOCK ResourceSpinLock;
+
+#endif
diff --git a/private/lsa/client/ssp/ntlm.c b/private/lsa/client/ssp/ntlm.c
new file mode 100644
index 000000000..e17a3bd68
--- /dev/null
+++ b/private/lsa/client/ssp/ntlm.c
@@ -0,0 +1,395 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: ntlm.c
+//
+// Contents: ntlm kernel-mode functions
+//
+//
+// History: 3/17/94 MikeSw Created
+//
+//------------------------------------------------------------------------
+#include <sspdrv.h>
+
+
+
+KSPIN_LOCK NtlmLock;
+PKernelContext pNtlmList;
+
+#pragma alloc_text(PAGE, NtlmInitialize)
+#pragma alloc_text(PAGE, NtlmDeleteKernelContext)
+#pragma alloc_text(PAGE, NtlmInitKernelContext)
+#pragma alloc_text(PAGE, NtlmMakeSignature)
+#pragma alloc_text(PAGE, NtlmVerifySignature)
+#pragma alloc_text(PAGE, NtlmSealMessage)
+#pragma alloc_text(PAGE, NtlmUnsealMessage)
+#pragma alloc_text(PAGE, NtlmGetToken)
+#pragma alloc_text(PAGE, NtlmQueryAttributes)
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: NtlmInitialize
+//
+// Synopsis: initializes the NTLM package functions
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS SEC_ENTRY
+NtlmInitialize(void)
+{
+ KeInitializeSpinLock(&NtlmLock);
+ pNtlmList = NULL;
+ return(STATUS_SUCCESS);
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: NtlmQueryAttributes
+//
+// Synopsis: Stub for QueryContextAttributes
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+NtlmQueryAttributes( ULONG ulContext,
+ ULONG dwAttribute,
+ PVOID pBuffer)
+{
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: MakeSignature
+//
+// Synopsis: makes a signature from the data field of the message
+// and stores it in the token field
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires: The client must allocate memory for the token field of
+// the message.
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+NtlmMakeSignature( ULONG ulContext,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo)
+{
+
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: VerifySignature
+//
+// Synopsis: Verifies that a signature on a message is correct
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+NtlmVerifySignature(ULONG ulContext,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOPUsed)
+{
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: NtlmSealMessage
+//
+// Synopsis: Stub for SealMessage
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+NtlmSealMessage( ULONG ulContext,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo)
+{
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: UnsealMessage
+//
+// Synopsis: unseals a message
+//
+// Effects: modifies the SECBUFFER_DATA section of pMessage
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+NtlmUnsealMessage( ULONG ulContext,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOPUsed)
+{
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: NtlmGetToken
+//
+// Synopsis: returns the token from a context
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS SEC_ENTRY
+NtlmGetToken( ULONG ulContext,
+ PHANDLE phToken,
+ PACCESS_TOKEN * pAccessToken)
+{
+ PKernelContext pContext;
+ NTSTATUS Status;
+
+
+ PAGED_CODE();
+
+ pContext = (PKernelContext) ulContext;
+
+ if (pContext == NULL)
+ {
+ DebugLog((DEB_ERROR,"Invalid handle 0x%x\n", ulContext));
+
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ // Now, after all that checking, let's actually try and set the
+ // thread impersonation token.
+
+
+ if (phToken != NULL)
+ {
+ *phToken = pContext->TokenHandle;
+ }
+
+ if (pAccessToken != NULL)
+ {
+ if (pContext->TokenHandle != NULL)
+ {
+ if (pContext->AccessToken == NULL)
+ {
+ Status = ObReferenceObjectByHandle(
+ pContext->TokenHandle,
+ TOKEN_IMPERSONATE,
+ NULL,
+ KeGetPreviousMode(),
+ (PVOID *) &pContext->AccessToken,
+ NULL // no handle information
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+ }
+ }
+
+ *pAccessToken = pContext->AccessToken;
+ }
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: NtlmInitKernelContext
+//
+// Synopsis: Initializes a kernel context with the session key
+// and possible token handle.
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS
+NtlmInitKernelContext(
+ IN PUCHAR UserSessionKey,
+ IN PUCHAR LanmanSessionKey,
+ IN HANDLE TokenHandle,
+ OUT PCtxtHandle ContextHandle
+ )
+{
+ PKernelContext pContext;
+ KIRQL OldIrql;
+
+ pContext = AllocContextRec();
+ if (!pContext)
+ {
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ RtlCopyMemory(
+ pContext->UserSessionKey,
+ UserSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ RtlCopyMemory(
+ pContext->LanmanSessionKey,
+ LanmanSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH
+ );
+
+ pContext->TokenHandle = TokenHandle;
+ pContext->AccessToken = NULL;
+ pContext->pPrev = NULL;
+
+ ContextHandle->dwLower = (ULONG) pContext;
+ ContextHandle->dwUpper = 0;
+
+ //
+ // Add it to the client record
+ //
+
+ AddKernelContext(&pNtlmList, &NtlmLock, pContext);
+ return(STATUS_SUCCESS);
+}
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: NtlmDeleteKernelContext
+//
+// Synopsis: Deletes a kernel context from the list of contexts
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS
+NtlmDeleteKernelContext( PCtxtHandle ContextHandle)
+{
+ SECURITY_STATUS scRet;
+
+
+ scRet = DeleteKernelContext(
+ &pNtlmList,
+ &NtlmLock,
+ (PKernelContext) ContextHandle->dwLower );
+
+ return(scRet);
+
+}
diff --git a/private/lsa/client/ssp/package.h b/private/lsa/client/ssp/package.h
new file mode 100644
index 000000000..54542e754
--- /dev/null
+++ b/private/lsa/client/ssp/package.h
@@ -0,0 +1,81 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: package.h
+//
+// Contents: kernel package structures
+//
+//
+// History: 3-18-94 MikeSw Created
+//
+//------------------------------------------------------------------------
+
+#ifndef __PACKAGE_H__
+#define __PACKAGE_H__
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspInitPackageFn)(void);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspDeleteContextFn)(PCtxtHandle ulContextId);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspInitContextFn)(
+ IN PUCHAR UserSessionKey,
+ IN PUCHAR LanmanSessionKey,
+ IN HANDLE TokenHandle,
+ OUT PCtxtHandle ContextHandle
+);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspMakeSignatureFn)( ULONG ulContextId,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspVerifySignatureFn)( ULONG ulContextId,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOP);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspSealMessageFn)( ULONG ulContextId,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspUnsealMessageFn)( ULONG ulContextId,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOP);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspGetTokenFn)( ULONG ulContextId,
+ HANDLE * phImpersonationToken,
+ PACCESS_TOKEN * pAccessToken);
+
+typedef SECURITY_STATUS
+(SEC_ENTRY KspQueryAttributesFn)( ULONG ulContextId,
+ ULONG ulAttribute,
+ PVOID pBuffer);
+
+
+
+KspInitPackageFn NtlmInitialize;
+KspInitContextFn NtlmInitKernelContext;
+KspDeleteContextFn NtlmDeleteKernelContext;
+KspMakeSignatureFn NtlmMakeSignature;
+KspVerifySignatureFn NtlmVerifySignature;
+KspSealMessageFn NtlmSealMessage;
+KspUnsealMessageFn NtlmUnsealMessage;
+KspGetTokenFn NtlmGetToken;
+KspQueryAttributesFn NtlmQueryAttributes;
+
+
+
+#endif __PACKAGE_H__
diff --git a/private/lsa/client/ssp/res.rc b/private/lsa/client/ssp/res.rc
new file mode 100644
index 000000000..b9cfeb529
--- /dev/null
+++ b/private/lsa/client/ssp/res.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "Kernel Security Support Provider Interface"
+#define VER_INTERNALNAME_STR "ksecdd.sys"
+
+#include "common.ver"
+
+
diff --git a/private/lsa/client/ssp/sources b/private/lsa/client/ssp/sources
new file mode 100644
index 000000000..1f6506ac1
--- /dev/null
+++ b/private/lsa/client/ssp/sources
@@ -0,0 +1,50 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+
+MINORCOMP=dd
+TARGETNAME=ksecdd
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=EXPORT_DRIVER
+
+# private\inc, private\lsa\inc;private\ntos\inc
+INCLUDES=..\..\..\inc;.;..\..\inc;..\..\..\ntos\inc
+
+
+SOURCES= \
+ ksecdd.c \
+ stubs.c \
+ support.c \
+ userstub.c \
+ memmgr.c \
+ connmgr.c \
+ ntlm.c \
+ context.c \
+ res.rc
+
+
+
+
+C_DEFINES= $(C_DEFINES) -DSECURITY_KERNEL -DUNICODE
+
diff --git a/private/lsa/client/ssp/sspdrv.h b/private/lsa/client/ssp/sspdrv.h
new file mode 100644
index 000000000..9887e8702
--- /dev/null
+++ b/private/lsa/client/ssp/sspdrv.h
@@ -0,0 +1,37 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: drvpch.hxx
+//
+// Contents: precompiled header include for ksecdd.sys
+//
+//
+// History: 3-17-94 MikeSw Created
+//
+//------------------------------------------------------------------------
+
+#ifndef __DRVPCH_H__
+#define __DRVPCH_H__
+
+
+#include <stdio.h>
+#include <ntos.h>
+#include <ntlsa.h>
+#define SECURITY_NTLM
+#include <security.h>
+#include <ntmsv1_0.h>
+#include <zwapi.h>
+#include <lmcons.h>
+#include <crypt.h>
+#include "connmgr.h"
+#include "ksecdd.h"
+#include "package.h"
+#include "memmgr.h"
+
+
+
+
+#endif // __DRVPCH_H__
diff --git a/private/lsa/client/ssp/stubs.c b/private/lsa/client/ssp/stubs.c
new file mode 100644
index 000000000..024d07acc
--- /dev/null
+++ b/private/lsa/client/ssp/stubs.c
@@ -0,0 +1,1565 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: stubs.cxx
+//
+// Contents: user-mode stubs for security API
+//
+//
+// History: 3/5/94 MikeSw Created
+//
+//------------------------------------------------------------------------
+#include <sspdrv.h>
+
+#pragma alloc_text(PAGE, AcquireCredentialsHandleW)
+#pragma alloc_text(PAGE, FreeCredentialsHandle)
+#pragma alloc_text(PAGE, InitializeSecurityContextW)
+#pragma alloc_text(PAGE, AcceptSecurityContext)
+#pragma alloc_text(PAGE, DeleteSecurityContext)
+#pragma alloc_text(PAGE, ApplyControlToken)
+#pragma alloc_text(PAGE, EnumerateSecurityPackagesW)
+#pragma alloc_text(PAGE, QuerySecurityPackageInfoW)
+#pragma alloc_text(PAGE, FreeContextBuffer)
+
+static CtxtHandle NullContext = {0,0};
+static CredHandle NullCredential = {0,0};
+static LUID lFake = {0, 0};
+static SECURITY_STRING sFake = {0, 0, NULL};
+static TOKEN_SOURCE KsecTokenSource = {"KSecDD", {0, 0} };
+
+#define NTLMSSP_REQUIRED_NEGOTIATE_FLAGS ( NTLMSSP_NEGOTIATE_UNICODE | \
+ NTLMSSP_REQUEST_INIT_RESPONSE )
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: AcquireCredentialsHandleW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+AcquireCredentialsHandleW(
+ PSECURITY_STRING pssPrincipal, // Name of principal
+ PSECURITY_STRING pssPackageName, // Name of package
+ unsigned long fCredentialUse, // Flags indicating use
+ void SEC_FAR * pvLogonId, // Pointer to logon ID
+ void SEC_FAR * pAuthData, // Package specific data
+ SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
+ void SEC_FAR * pvGetKeyArgument, // Value to pass to GetKey()
+ PCredHandle phCredential, // (out) Cred Handle
+ PTimeStamp ptsExpiry // (out) Lifetime (optional)
+ )
+{
+ SECURITY_STATUS scRet;
+ SECURITY_STRING Principal;
+ TimeStamp OptionalTimeStamp;
+ UNICODE_STRING PackageName;
+
+ PAGED_CODE();
+
+ if (!pssPackageName)
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ //
+ // We don't accept principal names either.
+ //
+
+ if (pssPrincipal)
+ {
+ return(SEC_E_UNKNOWN_CREDENTIALS);
+ }
+
+
+ //
+ // Make sure they want the NTLM security package
+ //
+ RtlInitUnicodeString(
+ &PackageName,
+ NTLMSP_NAME
+ );
+
+
+ if (!RtlEqualUnicodeString(
+ pssPackageName,
+ &PackageName,
+ TRUE))
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ //
+ // The credential handle is the logon id
+ //
+
+ if (fCredentialUse & SECPKG_CRED_OUTBOUND)
+ {
+ if (pvLogonId != NULL)
+ {
+ *phCredential = *(PCredHandle) pvLogonId;
+ }
+ else
+ {
+ return(SEC_E_UNKNOWN_CREDENTIALS);
+ }
+
+ }
+ else if (fCredentialUse & SECPKG_CRED_INBOUND)
+ {
+ //
+ // For inbound credentials, we will accept a logon id but
+ // we don't require it.
+ //
+
+ if (pvLogonId != NULL)
+ {
+ *phCredential = *(PCredHandle) pvLogonId;
+ }
+ else
+ {
+ *phCredential = NullCredential;
+ }
+
+ }
+ else
+ {
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+ }
+
+
+ return(SEC_E_OK);
+
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: FreeCredentialsHandle
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+FreeCredentialsHandle(
+ PCredHandle phCredential // Handle to free
+ )
+{
+ PAGED_CODE();
+
+ //
+ // Since the credential handle is just the LogonId, do nothing.
+ //
+
+ return(SEC_E_OK);
+}
+
+
+VOID
+PutString(
+ OUT PSTRING Destination,
+ IN PSTRING Source,
+ IN PVOID Base,
+ IN OUT PUCHAR * Where
+ )
+{
+ Destination->Buffer = (PCHAR) *Where - (ULONG) Base;
+ Destination->Length =
+ Source->Length;
+ Destination->MaximumLength =
+ Source->Length;
+
+ RtlCopyMemory(
+ *Where,
+ Source->Buffer,
+ Source->Length
+ );
+ *Where += Source->Length;
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitializeSecurityContextW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+InitializeSecurityContextW(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ PSECURITY_STRING pssTargetName, // Name of target
+ unsigned long fContextReq, // Context Requirements
+ unsigned long Reserved1, // Reserved, MBZ
+ unsigned long TargetDataRep, // Data rep of target
+ PSecBufferDesc pInput, // Input Buffers
+ unsigned long Reserved2, // Reserved, MBZ
+ PCtxtHandle phNewContext, // (out) New Context handle
+ PSecBufferDesc pOutput, // (inout) Output Buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ )
+{
+ SECURITY_STATUS scRet;
+ PMSV1_0_GETCHALLENRESP_REQUEST ChallengeRequest = NULL;
+ ULONG ChallengeRequestSize;
+ PMSV1_0_GETCHALLENRESP_RESPONSE ChallengeResponse = NULL;
+ ULONG ChallengeResponseSize;
+ PCHALLENGE_MESSAGE ChallengeMessage = NULL;
+ ULONG ChallengeMessageSize;
+ PNTLM_CHALLENGE_MESSAGE NtlmChallengeMessage = NULL;
+ ULONG NtlmChallengeMessageSize;
+ PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL;
+ ULONG AuthenticateMessageSize;
+ PNTLM_INITIALIZE_RESPONSE NtlmInitializeResponse = NULL;
+ PClient Client = NULL;
+ UNICODE_STRING PasswordToUse;
+ UNICODE_STRING UserNameToUse;
+ UNICODE_STRING DomainNameToUse;
+ ULONG ParameterControl = USE_PRIMARY_PASSWORD |
+ RETURN_PRIMARY_USERNAME |
+ RETURN_PRIMARY_LOGON_DOMAINNAME;
+
+ NTSTATUS FinalStatus = STATUS_SUCCESS;
+ PUCHAR Where;
+ PSecBuffer AuthenticationToken = NULL;
+ PSecBuffer InitializeResponseToken = NULL;
+ BOOLEAN UseSuppliedCreds = FALSE;
+
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString(
+ &PasswordToUse,
+ NULL
+ );
+
+ RtlInitUnicodeString(
+ &UserNameToUse,
+ NULL
+ );
+
+ RtlInitUnicodeString(
+ &DomainNameToUse,
+ NULL
+ );
+
+ //
+ // Check for valid sizes, pointers, etc.:
+ //
+
+
+ if (!phCredential)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ //
+ // Check that we can indeed call the LSA and get the client
+ // handle to it.
+ //
+
+ scRet = IsOkayToExec(&Client);
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ //
+ // Locate the buffers with the input data
+ //
+
+ if (!GetTokenBuffer(
+ pInput,
+ 0, // get the first security token
+ (PVOID *) &ChallengeMessage,
+ &ChallengeMessageSize,
+ TRUE // may be readonly
+ ))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // If we are using supplied creds, get them now too.
+ //
+
+
+ if (fContextReq & ISC_REQ_USE_SUPPLIED_CREDS)
+ {
+ if (!GetTokenBuffer(
+ pInput,
+ 1, // get the second security token
+ (PVOID *) &NtlmChallengeMessage,
+ &NtlmChallengeMessageSize,
+ TRUE // may be readonly
+ ))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+ else
+ {
+ UseSuppliedCreds = TRUE;
+ }
+
+ }
+
+ //
+ // Get the output tokens
+ //
+
+ if (!GetSecurityToken(
+ pOutput,
+ 0,
+ &AuthenticationToken) ||
+ !GetSecurityToken(
+ pOutput,
+ 1,
+ &InitializeResponseToken ) )
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // Make sure the sizes are o.k.
+ //
+
+ if ((ChallengeMessageSize < sizeof(CHALLENGE_MESSAGE)) ||
+ (UseSuppliedCreds &&
+ !(NtlmChallengeMessageSize < sizeof(NTLM_CHALLENGE_MESSAGE))))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ }
+
+ //
+ // Make sure the caller wants us to allocate memory:
+ //
+
+ if (!(fContextReq & ISC_REQ_ALLOCATE_MEMORY))
+ {
+ scRet = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+//
+// BUGBUG: allow calls requesting PROMPT_FOR_CREDS to go through.
+// We won't prompt, but we will setup a context properly.
+//
+
+// if ((fContextReq & ISC_REQ_PROMPT_FOR_CREDS) != 0)
+// {
+// scRet = SEC_E_UNSUPPORTED_FUNCTION;
+// goto Cleanup;
+// }
+
+ //
+ // Verify the validity of the challenge message.
+ //
+
+ if (strncmp(
+ ChallengeMessage->Signature,
+ NTLMSSP_SIGNATURE,
+ sizeof(NTLMSSP_SIGNATURE)))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if (ChallengeMessage->MessageType != NtLmChallenge)
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if (ChallengeMessage->NegotiateFlags & NTLMSSP_REQUIRED_NEGOTIATE_FLAGS !=
+ NTLMSSP_REQUIRED_NEGOTIATE_FLAGS)
+ {
+ scRet = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ if ((ChallengeMessage->NegotiateFlags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY) != 0)
+ {
+ ParameterControl |= RETURN_NON_NT_USER_SESSION_KEY;
+ }
+
+ if ((fContextReq & ISC_REQ_USE_SUPPLIED_CREDS) != 0)
+ {
+ if ( NtlmChallengeMessage->Password.Buffer != NULL)
+ {
+ ParameterControl &= ~USE_PRIMARY_PASSWORD;
+ PasswordToUse = NtlmChallengeMessage->Password;
+ PasswordToUse.Buffer = (LPWSTR) ((PCHAR) PasswordToUse.Buffer +
+ (ULONG) NtlmChallengeMessage);
+ }
+
+ if (NtlmChallengeMessage->UserName.Length != 0)
+ {
+ UserNameToUse = NtlmChallengeMessage->UserName;
+ UserNameToUse.Buffer = (LPWSTR) ((PCHAR) UserNameToUse.Buffer +
+ (ULONG) NtlmChallengeMessage);
+ ParameterControl &= ~RETURN_PRIMARY_USERNAME;
+ }
+ if (NtlmChallengeMessage->DomainName.Length != 0)
+ {
+ DomainNameToUse = NtlmChallengeMessage->DomainName;
+ DomainNameToUse.Buffer = (LPWSTR) ((PCHAR) DomainNameToUse.Buffer +
+ (ULONG) NtlmChallengeMessage);
+ ParameterControl &= ~RETURN_PRIMARY_LOGON_DOMAINNAME;
+ }
+
+ }
+
+ //
+ // Package up the parameter for a call to the LSA.
+ //
+
+ ChallengeRequestSize = sizeof(MSV1_0_GETCHALLENRESP_REQUEST) +
+ PasswordToUse.Length;
+
+ scRet = ZwAllocateVirtualMemory(
+ NtCurrentProcess(),
+ &ChallengeRequest,
+ 0L,
+ &ChallengeRequestSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+
+
+ //
+ // Build the challenge request message.
+ //
+
+ ChallengeRequest->MessageType = MsV1_0Lm20GetChallengeResponse;
+ ChallengeRequest->ParameterControl = ParameterControl;
+ ChallengeRequest->LogonId = * (PLUID) phCredential;
+ RtlCopyMemory(
+ ChallengeRequest->ChallengeToClient,
+ ChallengeMessage->Challenge,
+ MSV1_0_CHALLENGE_LENGTH
+ );
+ if ((ParameterControl & USE_PRIMARY_PASSWORD) == 0)
+ {
+ ChallengeRequest->Password.Buffer = (LPWSTR) (ChallengeRequest+1);
+ RtlCopyMemory(
+ ChallengeRequest->Password.Buffer,
+ PasswordToUse.Buffer,
+ PasswordToUse.Length
+ );
+ ChallengeRequest->Password.Length = PasswordToUse.Length;
+ ChallengeRequest->Password.MaximumLength = PasswordToUse.Length;
+ }
+
+ //
+ // Call the LSA to get the challenge response.
+ //
+
+ scRet = LsaCallAuthenticationPackage(
+ Client->hPort,
+ PackageId,
+ ChallengeRequest,
+ ChallengeRequestSize,
+ &ChallengeResponse,
+ &ChallengeResponseSize,
+ &FinalStatus
+ );
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+ if (!NT_SUCCESS(FinalStatus))
+ {
+ scRet = FinalStatus;
+ goto Cleanup;
+ }
+
+ ASSERT(ChallengeResponse->MessageType == MsV1_0Lm20GetChallengeResponse);
+ //
+ // Now prepare the output message.
+ //
+
+ if (UserNameToUse.Buffer == NULL)
+ {
+ UserNameToUse = ChallengeResponse->UserName;
+ }
+ if (DomainNameToUse.Buffer == NULL)
+ {
+ DomainNameToUse = ChallengeResponse->LogonDomainName;
+ }
+
+ AuthenticateMessageSize = sizeof(AUTHENTICATE_MESSAGE) +
+ UserNameToUse.Length +
+ DomainNameToUse.Length +
+ ChallengeResponse->CaseSensitiveChallengeResponse.Length +
+ ChallengeResponse->CaseInsensitiveChallengeResponse.Length;
+
+ //
+ // BUGBUG: where do I get the workstation name from?
+ //
+
+ AuthenticateMessage = (PAUTHENTICATE_MESSAGE) SecAllocate(AuthenticateMessageSize);
+ if (AuthenticateMessage == NULL)
+ {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ Where = (PUCHAR) (AuthenticateMessage + 1);
+ RtlCopyMemory(
+ AuthenticateMessage->Signature,
+ NTLMSSP_SIGNATURE,
+ sizeof(NTLMSSP_SIGNATURE)
+ );
+ AuthenticateMessage->MessageType = NtLmAuthenticate;
+
+ PutString(
+ &AuthenticateMessage->LmChallengeResponse,
+ &ChallengeResponse->CaseInsensitiveChallengeResponse,
+ AuthenticateMessage,
+ &Where
+ );
+
+ PutString(
+ &AuthenticateMessage->NtChallengeResponse,
+ &ChallengeResponse->CaseSensitiveChallengeResponse,
+ AuthenticateMessage,
+ &Where
+ );
+
+ PutString(
+ &AuthenticateMessage->DomainName,
+ (PSTRING) &DomainNameToUse,
+ AuthenticateMessage,
+ &Where
+ );
+
+ PutString(
+ &AuthenticateMessage->UserName,
+ (PSTRING) &UserNameToUse,
+ AuthenticateMessage,
+ &Where
+ );
+
+ //
+ // BUGBUG: no workstation name to fill in.
+ //
+
+ AuthenticateMessage->Workstation.Length = 0;
+ AuthenticateMessage->Workstation.MaximumLength = 0;
+ AuthenticateMessage->Workstation.Buffer = NULL;
+
+
+ //
+ // Build the initialize response.
+ //
+
+ NtlmInitializeResponse = (PNTLM_INITIALIZE_RESPONSE) SecAllocate(sizeof(NTLM_INITIALIZE_RESPONSE));
+ if (NtlmInitializeResponse == NULL)
+ {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+
+ RtlCopyMemory(
+ NtlmInitializeResponse->UserSessionKey,
+ ChallengeResponse->UserSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ RtlCopyMemory(
+ NtlmInitializeResponse->LanmanSessionKey,
+ ChallengeResponse->LanmanSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH
+ );
+
+ //
+ // Fill in the output buffers now.
+ //
+
+ AuthenticationToken->pvBuffer = AuthenticateMessage;
+ AuthenticationToken->cbBuffer = AuthenticateMessageSize;
+ InitializeResponseToken->pvBuffer = NtlmInitializeResponse;
+ InitializeResponseToken->cbBuffer = sizeof(NTLM_INITIALIZE_RESPONSE);
+
+
+ //
+ // Make a local context for this
+ //
+
+ scRet = NtlmInitKernelContext(
+ NtlmInitializeResponse->UserSessionKey,
+ NtlmInitializeResponse->LanmanSessionKey,
+ NULL, // no token,
+ phNewContext
+ );
+
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+ scRet = SEC_E_OK;
+
+
+
+
+Cleanup:
+
+ if (ChallengeRequest != NULL)
+ {
+ ZwFreeVirtualMemory(
+ NtCurrentProcess(),
+ &ChallengeRequest,
+ &ChallengeRequestSize,
+ MEM_RELEASE
+ );
+ }
+
+ if (ChallengeResponse != NULL)
+ {
+ LsaFreeReturnBuffer( ChallengeResponse );
+ }
+
+ if (!NT_SUCCESS(scRet))
+ {
+ if (AuthenticateMessage != NULL)
+ {
+ SecFree(AuthenticateMessage);
+ }
+ if (NtlmInitializeResponse != NULL)
+ {
+ SecFree(NtlmInitializeResponse);
+ }
+ }
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: AcceptSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+AcceptSecurityContext(
+ PCredHandle phCredential, // Cred to base context
+ PCtxtHandle phContext, // Existing context (OPT)
+ PSecBufferDesc pInput, // Input buffer
+ unsigned long fContextReq, // Context Requirements
+ unsigned long TargetDataRep, // Target Data Rep
+ PCtxtHandle phNewContext, // (out) New context handle
+ PSecBufferDesc pOutput, // (inout) Output buffers
+ unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes
+ PTimeStamp ptsExpiry // (out) Life span (OPT)
+ )
+{
+ SECURITY_STATUS scRet;
+ NTSTATUS SubStatus;
+ PAUTHENTICATE_MESSAGE AuthenticateMessage;
+ ULONG AuthenticateMessageSize;
+ PNTLM_AUTHENTICATE_MESSAGE NtlmAuthenticateMessage;
+ ULONG NtlmAuthenticateMessageSize;
+ PNTLM_ACCEPT_RESPONSE NtlmAcceptResponse = NULL;
+ PMSV1_0_LM20_LOGON LogonBuffer = NULL;
+ ULONG LogonBufferSize;
+ PMSV1_0_LM20_LOGON_PROFILE LogonProfile = NULL;
+ ULONG LogonProfileSize;
+ PSecBuffer AcceptResponseToken = NULL;
+ PClient Client = NULL;
+ ANSI_STRING SourceName;
+ LUID LogonId;
+ LUID UNALIGNED * TempLogonId;
+ LARGE_INTEGER UNALIGNED * TempKickoffTime;
+ HANDLE TokenHandle = NULL;
+ QUOTA_LIMITS Quotas;
+ PUCHAR Where;
+ STRING DomainName;
+ STRING UserName;
+ STRING Workstation;
+ STRING NtChallengeResponse;
+ STRING LmChallengeResponse;
+ ULONG EffectivePackageId;
+
+
+ RtlInitString(
+ &SourceName,
+ NULL
+ );
+
+ PAGED_CODE();
+
+ //
+ // Check for valid sizes, pointers, etc.:
+ //
+
+
+ if (!phCredential)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+
+ //
+ // Check that we can indeed call the LSA and get the client
+ // handle to it.
+ //
+
+ scRet = IsOkayToExec(&Client);
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ //
+ // Locate the buffers with the input data
+ //
+
+ if (!GetTokenBuffer(
+ pInput,
+ 0, // get the first security token
+ (PVOID *) &AuthenticateMessage,
+ &AuthenticateMessageSize,
+ TRUE // may be readonly
+ ) ||
+ (!GetTokenBuffer(
+ pInput,
+ 1, // get the second security token
+ (PVOID *) &NtlmAuthenticateMessage,
+ &NtlmAuthenticateMessageSize,
+ TRUE // may be readonly
+ )))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // Get the output tokens
+ //
+
+ if (!GetSecurityToken(
+ pOutput,
+ 0,
+ &AcceptResponseToken ) )
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // Make sure the sizes are o.k.
+ //
+
+ if ((AuthenticateMessageSize < sizeof(AUTHENTICATE_MESSAGE)) ||
+ (NtlmAuthenticateMessageSize < sizeof(NTLM_AUTHENTICATE_MESSAGE)))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ }
+
+ //
+ // Make sure the caller does not want us to allocate memory:
+ //
+
+ if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
+ {
+ scRet = SEC_E_UNSUPPORTED_FUNCTION;
+ goto Cleanup;
+ }
+
+ if (AcceptResponseToken->cbBuffer < sizeof(NTLM_ACCEPT_RESPONSE))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+
+ //
+ // Verify the validity of the Authenticate message.
+ //
+
+ if (strncmp(
+ AuthenticateMessage->Signature,
+ NTLMSSP_SIGNATURE,
+ sizeof(NTLMSSP_SIGNATURE)))
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ if (AuthenticateMessage->MessageType != NtLmAuthenticate)
+ {
+ scRet = SEC_E_INVALID_TOKEN;
+ goto Cleanup;
+ }
+
+ //
+ // Fixup the buffer pointers
+ //
+
+ UserName = AuthenticateMessage->UserName;
+ UserName.Buffer = UserName.Buffer + (ULONG) AuthenticateMessage;
+
+ DomainName = AuthenticateMessage->DomainName;
+ DomainName.Buffer = DomainName.Buffer + (ULONG) AuthenticateMessage;
+
+ Workstation = AuthenticateMessage->Workstation;
+ Workstation.Buffer = Workstation.Buffer + (ULONG) AuthenticateMessage;
+
+ NtChallengeResponse = AuthenticateMessage->NtChallengeResponse;
+ NtChallengeResponse.Buffer = NtChallengeResponse.Buffer + (ULONG) AuthenticateMessage;
+
+ LmChallengeResponse = AuthenticateMessage->LmChallengeResponse;
+ LmChallengeResponse.Buffer = LmChallengeResponse.Buffer + (ULONG) AuthenticateMessage;
+
+ //
+ // Allocate a buffer to pass into LsaLogonUser
+ //
+
+ LogonBufferSize = sizeof(MSV1_0_LM20_LOGON) +
+ UserName.Length +
+ DomainName.Length +
+ Workstation.Length +
+ LmChallengeResponse.Length +
+ NtChallengeResponse.Length;
+
+ scRet = ZwAllocateVirtualMemory(
+ NtCurrentProcess(),
+ &LogonBuffer,
+ 0L,
+ &LogonBufferSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the fixed-length portions
+ //
+
+ LogonBuffer->MessageType = MsV1_0NetworkLogon;
+
+ RtlCopyMemory(
+ LogonBuffer->ChallengeToClient,
+ NtlmAuthenticateMessage->ChallengeToClient,
+ MSV1_0_CHALLENGE_LENGTH
+ );
+
+ //
+ // Fill in the variable length pieces
+ //
+
+ Where = (PUCHAR) (LogonBuffer + 1);
+
+ PutString(
+ (PSTRING) &LogonBuffer->LogonDomainName,
+ &DomainName,
+ 0,
+ &Where
+ );
+
+ PutString(
+ (PSTRING) &LogonBuffer->UserName,
+ &UserName,
+ 0,
+ &Where
+ );
+
+ PutString(
+ (PSTRING) &LogonBuffer->Workstation,
+ &Workstation,
+ 0,
+ &Where
+ );
+
+ PutString(
+ (PSTRING) &LogonBuffer->CaseSensitiveChallengeResponse,
+ &NtChallengeResponse,
+ 0,
+ &Where
+ );
+
+ PutString(
+ (PSTRING) &LogonBuffer->CaseInsensitiveChallengeResponse,
+ &LmChallengeResponse,
+ 0,
+ &Where
+ );
+
+ LogonBuffer->ParameterControl = MSV1_0_CLEARTEXT_PASSWORD_ALLOWED |
+ NtlmAuthenticateMessage->ParameterControl;
+
+ scRet = RtlUnicodeStringToAnsiString(
+ &SourceName,
+ (PUNICODE_STRING) &Workstation,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+
+ if ( fContextReq & ASC_REQ_LICENSING )
+ {
+ EffectivePackageId = PackageId | LSA_CALL_LICENSE_SERVER ;
+ }
+ else
+ {
+ EffectivePackageId = PackageId ;
+ }
+
+ scRet = LsaLogonUser(
+ Client->hPort,
+ &SourceName,
+ Network,
+ EffectivePackageId,
+ LogonBuffer,
+ LogonBufferSize,
+ NULL, // token groups
+ &KsecTokenSource,
+ (PVOID *) &LogonProfile,
+ &LogonProfileSize,
+ &LogonId,
+ &TokenHandle,
+ &Quotas,
+ &SubStatus
+ );
+
+ if (scRet == STATUS_ACCOUNT_RESTRICTION)
+ {
+ scRet = SubStatus;
+ }
+ if (!NT_SUCCESS(scRet))
+ {
+ //
+ // LsaLogonUser returns garbage for the token if it fails,
+ // so zero it now so we don't try to close it later.
+ //
+
+ TokenHandle = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Create the kernel context
+ //
+
+ scRet = NtlmInitKernelContext(
+ LogonProfile->UserSessionKey,
+ LogonProfile->LanmanSessionKey,
+ TokenHandle,
+ phNewContext
+ );
+
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+
+ TokenHandle = NULL;
+
+ //
+ // Allocate the return buffer.
+ //
+
+
+ NtlmAcceptResponse = (PNTLM_ACCEPT_RESPONSE) AcceptResponseToken->pvBuffer;
+ if (NtlmAcceptResponse == NULL)
+ {
+ scRet = SEC_E_INSUFFICIENT_MEMORY;
+ goto Cleanup;
+ }
+
+ TempLogonId = (LUID UNALIGNED *) &NtlmAcceptResponse->LogonId;
+ *TempLogonId = LogonId;
+ NtlmAcceptResponse->UserFlags = LogonProfile->UserFlags;
+
+ RtlCopyMemory(
+ NtlmAcceptResponse->UserSessionKey,
+ LogonProfile->UserSessionKey,
+ MSV1_0_USER_SESSION_KEY_LENGTH
+ );
+
+ RtlCopyMemory(
+ NtlmAcceptResponse->LanmanSessionKey,
+ LogonProfile->LanmanSessionKey,
+ MSV1_0_LANMAN_SESSION_KEY_LENGTH
+ );
+
+ TempKickoffTime = (LARGE_INTEGER UNALIGNED *) &NtlmAcceptResponse->KickoffTime;
+ *TempKickoffTime = LogonProfile->KickOffTime;
+
+ AcceptResponseToken->cbBuffer = sizeof(NTLM_ACCEPT_RESPONSE);
+
+ if ( fContextReq & ASC_REQ_LICENSING )
+ {
+ *pfContextAttr = ASC_RET_ALLOCATED_MEMORY | ASC_RET_LICENSING ;
+ }
+ else
+ {
+ *pfContextAttr = ASC_RET_ALLOCATED_MEMORY;
+ }
+
+ *ptsExpiry = LogonProfile->LogoffTime;
+ scRet = SEC_E_OK;
+
+
+Cleanup:
+ if (SourceName.Buffer != NULL)
+ {
+ RtlFreeAnsiString(&SourceName);
+ }
+
+ if (LogonBuffer != NULL)
+ {
+ ZwFreeVirtualMemory(
+ NtCurrentProcess(),
+ &LogonBuffer,
+ &LogonBufferSize,
+ MEM_RELEASE
+ );
+ }
+
+ if (LogonProfile != NULL)
+ {
+ LsaFreeReturnBuffer(LogonProfile);
+ }
+
+
+ if (TokenHandle != NULL)
+ {
+ NtClose(TokenHandle);
+ }
+
+ return(scRet);
+
+}
+
+
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: DeleteSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+DeleteSecurityContext(
+ PCtxtHandle phContext // Context to delete
+ )
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+
+ // For now, just delete the LSA context:
+
+ if (!phContext)
+ {
+ return(SEC_E_INVALID_HANDLE);
+ }
+
+ scRet = NtlmDeleteKernelContext(phContext);
+
+
+ return(scRet);
+
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: ApplyControlToken
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+ApplyControlToken(
+ PCtxtHandle phContext, // Context to modify
+ PSecBufferDesc pInput // Input token to apply
+ )
+{
+ PAGED_CODE();
+
+
+
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+
+
+}
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: EnumerateSecurityPackagesW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+EnumerateSecurityPackagesW(
+ unsigned long SEC_FAR * pcPackages, // Receives num. packages
+ PSecPkgInfo SEC_FAR * ppPackageInfo // Receives array of info
+ )
+{
+ ULONG PackageInfoSize;
+ PSecPkgInfoW PackageInfo = NULL;
+ PUCHAR Where;
+
+ PAGED_CODE();
+
+ //
+ // Figure out the size of the returned data
+ //
+
+ PackageInfoSize = sizeof(SecPkgInfoW) +
+ sizeof(NTLMSP_NAME) +
+ sizeof(NTLMSP_COMMENT);
+
+ PackageInfo = (PSecPkgInfoW) SecAllocate(PackageInfoSize);
+
+ if (PackageInfo == NULL)
+ {
+ return(SEC_E_INSUFFICIENT_MEMORY);
+ }
+
+ //
+ // Fill in the fixed length fields
+ //
+
+ PackageInfo->fCapabilities = SECPKG_FLAG_CONNECTION |
+ SECPKG_FLAG_TOKEN_ONLY;
+ PackageInfo->wVersion = NTLMSP_VERSION;
+ PackageInfo->wRPCID = NTLMSP_RPCID;
+ PackageInfo->cbMaxToken = NTLMSSP_MAX_MESSAGE_SIZE;
+
+ //
+ // Fill in the fields
+ //
+
+ Where = (PUCHAR) (PackageInfo+1);
+ PackageInfo->Name = (LPWSTR) Where;
+ RtlCopyMemory(
+ PackageInfo->Name,
+ NTLMSP_NAME,
+ sizeof(NTLMSP_NAME)
+ );
+ Where += sizeof(NTLMSP_NAME);
+
+ PackageInfo->Comment = (LPWSTR) Where;
+ RtlCopyMemory(
+ PackageInfo->Comment,
+ NTLMSP_COMMENT,
+ sizeof(NTLMSP_COMMENT)
+ );
+ Where += sizeof(NTLMSP_COMMENT);
+
+
+ *pcPackages = 1;
+ *ppPackageInfo = PackageInfo;
+ return(SEC_E_OK);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QuerySecurityPackageInfoW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+QuerySecurityPackageInfoW(
+ PSECURITY_STRING pssPackageName, // Name of package
+ PSecPkgInfo * ppPackageInfo // Receives package info
+ )
+{
+
+ UNICODE_STRING PackageName;
+ ULONG PackageCount;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString(
+ &PackageName,
+ NTLMSP_NAME
+ );
+
+
+ if (!RtlEqualUnicodeString(
+ pssPackageName,
+ &PackageName,
+ TRUE // case insensitive
+ ))
+ {
+ return(SEC_E_SECPKG_NOT_FOUND);
+ }
+
+ return(EnumerateSecurityPackages(&PackageCount,ppPackageInfo));
+
+}
+
+
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: FreeContextBuffer
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+SECURITY_STATUS SEC_ENTRY
+FreeContextBuffer(
+ void SEC_FAR * pvContextBuffer
+ )
+{
+ PAGED_CODE();
+
+ SecFree(pvContextBuffer);
+
+ return(SEC_E_OK);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: GetSecurityUserData
+//
+// Synopsis: retrieves information about a logged on user.
+//
+// Effects: allocates memory to be freed with FreeContextBuffer
+//
+// Arguments: pLogonId - Logon id of the user in question
+// fFlags - Indicates whether the caller want Cairo style names
+// or NT styles names. For NT, this is ignored.
+// ppUserInfo - Recieves information about user
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+GetSecurityUserInfo(
+ IN PLUID pLogonId,
+ IN ULONG fFlags,
+ OUT PSecurityUserData * ppUserInfo)
+{
+ NTSTATUS Status, FinalStatus;
+ PVOID GetInfoBuffer = NULL;
+ PVOID GetInfoResponseBuffer = NULL;
+ PMSV1_0_GETUSERINFO_REQUEST GetInfoRequest;
+ PMSV1_0_GETUSERINFO_RESPONSE GetInfoResponse;
+ ULONG ResponseSize;
+ ULONG RegionSize = sizeof(MSV1_0_GETUSERINFO_REQUEST);
+ PSecurityUserData UserInfo = NULL;
+ ULONG UserInfoSize;
+ PUCHAR Where;
+ SECURITY_STATUS scRet;
+ PClient Client = NULL;
+
+
+ scRet = IsOkayToExec(&Client);
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+
+ //
+ // Allocate virtual memory for the response buffer.
+ //
+
+ Status = ZwAllocateVirtualMemory(
+ NtCurrentProcess(),
+ &GetInfoBuffer, 0L,
+ &RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+
+ GetInfoRequest = GetInfoBuffer;
+
+ if (!NT_SUCCESS(Status)) {
+ scRet = Status;
+ goto Cleanup;
+ }
+
+ GetInfoRequest->MessageType = MsV1_0GetUserInfo;
+
+ RtlCopyLuid(&GetInfoRequest->LogonId, pLogonId);
+
+ Status = LsaCallAuthenticationPackage(
+ Client->hPort,
+ PackageId,
+ GetInfoRequest,
+ RegionSize,
+ &GetInfoResponseBuffer,
+ &ResponseSize,
+ &FinalStatus);
+
+ GetInfoResponse = GetInfoResponseBuffer;
+
+ if (!NT_SUCCESS(Status)) {
+ GetInfoResponseBuffer = NULL;
+ scRet = Status;
+ goto Cleanup;
+ }
+
+
+ if (!NT_SUCCESS(FinalStatus)) {
+ scRet = FinalStatus;
+ goto Cleanup;
+ }
+
+ ASSERT(GetInfoResponse->MessageType == MsV1_0GetUserInfo);
+
+ //
+ // Build a SecurityUserData
+ //
+
+ UserInfoSize = sizeof(SecurityUserData) +
+ GetInfoResponse->UserName.MaximumLength +
+ GetInfoResponse->LogonDomainName.MaximumLength +
+ GetInfoResponse->LogonServer.MaximumLength +
+ RtlLengthSid(GetInfoResponse->UserSid);
+
+
+
+ scRet = ZwAllocateVirtualMemory(
+ NtCurrentProcess(),
+ &UserInfo,
+ 0L,
+ &UserInfoSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS(scRet))
+ {
+ goto Cleanup;
+ }
+
+ //
+ // Pack in the SID first, to respectalignment boundaries.
+ //
+
+ Where = (PUCHAR) (UserInfo + 1);
+ UserInfo->pSid = (PSID) (Where);
+ RtlCopySid(
+ UserInfoSize,
+ Where,
+ GetInfoResponse->UserSid
+ );
+ Where += RtlLengthSid(Where);
+
+ //
+ // Pack in the strings
+ //
+
+ PutString(
+ (PSTRING) &UserInfo->UserName,
+ (PSTRING) &GetInfoResponse->UserName,
+ 0,
+ &Where
+ );
+
+ PutString(
+ (PSTRING) &UserInfo->LogonDomainName,
+ (PSTRING) &GetInfoResponse->LogonDomainName,
+ 0,
+ &Where
+ );
+
+ PutString(
+ (PSTRING) &UserInfo->LogonServer,
+ (PSTRING) &GetInfoResponse->LogonServer,
+ 0,
+ &Where
+ );
+
+ *ppUserInfo = UserInfo;
+ UserInfo = NULL;
+ scRet = STATUS_SUCCESS;
+
+Cleanup:
+ if (GetInfoRequest != NULL)
+ {
+ ZwFreeVirtualMemory(
+ NtCurrentProcess(),
+ &GetInfoRequest,
+ &RegionSize,
+ MEM_RELEASE
+ );
+
+ }
+
+ if (UserInfo != NULL)
+ {
+ FreeContextBuffer(UserInfo);
+ }
+
+ if (GetInfoResponseBuffer != NULL)
+ {
+ LsaFreeReturnBuffer(GetInfoResponseBuffer);
+ }
+
+ return(scRet);
+}
+
diff --git a/private/lsa/client/ssp/support.c b/private/lsa/client/ssp/support.c
new file mode 100644
index 000000000..e62646a80
--- /dev/null
+++ b/private/lsa/client/ssp/support.c
@@ -0,0 +1,432 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: support.cxx
+//
+// Contents: support routines for ksecdd.sys
+//
+//
+// History: 3-7-94 Created MikeSw
+//
+//------------------------------------------------------------------------
+
+#include <sspdrv.h>
+
+
+
+//
+// Global Variables:
+//
+
+
+BOOLEAN fInitialized = FALSE;
+
+
+
+
+SecurityFunctionTable SecTable = {SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION,
+ EnumerateSecurityPackages,
+ NULL, // LogonUser,
+ AcquireCredentialsHandle,
+ FreeCredentialsHandle,
+ NULL, // QueryCredentialAttributes,
+ InitializeSecurityContext,
+ AcceptSecurityContext,
+ CompleteAuthToken,
+ DeleteSecurityContext,
+ ApplyControlToken,
+ QueryContextAttributes,
+ ImpersonateSecurityContext,
+ RevertSecurityContext,
+ MakeSignature,
+ VerifySignature,
+ FreeContextBuffer,
+ NULL, // QuerySecurityPackageInfo
+ SealMessage,
+ UnsealMessage
+ };
+
+
+
+
+#pragma alloc_text(PAGE, SecAllocate)
+#pragma alloc_text(PAGE, SecFree)
+#pragma alloc_text(PAGE, IsOkayToExec)
+#pragma alloc_text(PAGE, InitSecurityInterfaceW)
+#pragma alloc_text(PAGE, MapSecurityError)
+#pragma alloc_text(PAGE, GetTokenBuffer)
+#pragma alloc_text(PAGE, GetSecurityToken)
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: SecAllocate
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+VOID * SEC_ENTRY
+SecAllocate(ULONG cbMemory)
+{
+ PAGED_CODE();
+ return(ExAllocatePool(PagedPool, cbMemory));
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: SecFree
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+void SEC_ENTRY
+SecFree(PVOID pvMemory)
+{
+ PAGED_CODE();
+ ExFreePool(pvMemory);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: IsOkayToExec
+//
+// Synopsis: Determines if it is okay to make a call to the SPM
+//
+// Effects: Binds if necessary to the SPM
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes: uses IsSPMgrReady and InitState
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS
+IsOkayToExec(PClient * ppClient)
+{
+ SECURITY_STATUS scRet;
+ PClient pClient;
+
+ PAGED_CODE();
+ if (NT_SUCCESS(LocateClient(&pClient)))
+ {
+ if (ppClient)
+ *ppClient = pClient;
+ return(STATUS_SUCCESS);
+ }
+ scRet = CreateClient(&pClient);
+ if (!NT_SUCCESS(scRet))
+ {
+ return(scRet);
+ }
+ if (ppClient)
+ *ppClient = pClient;
+ return(STATUS_SUCCESS);
+}
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitSecurityInterfaceW
+//
+// Synopsis: returns function table of all the security function and,
+// more importantly, create a client session.
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+
+PSecurityFunctionTableW SEC_ENTRY
+InitSecurityInterfaceW(void)
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+ scRet = IsOkayToExec(NULL);
+ if (!NT_SUCCESS(scRet))
+ {
+ DebugLog((DEB_ERROR, "Failed to init security interface: 0x%x\n",scRet));
+ return(NULL);
+ }
+ return(&SecTable);
+}
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: MapSecurityError
+//
+// Synopsis: maps a HRESULT from the security interface to a NTSTATUS
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+NTSTATUS SEC_ENTRY
+MapSecurityError(HRESULT Error)
+{
+ PAGED_CODE();
+ return((NTSTATUS) Error);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: GetTokenBuffer
+//
+// Synopsis:
+//
+// This routine parses a Token Descriptor and pulls out the useful
+// information.
+//
+// Effects:
+//
+// Arguments:
+//
+// TokenDescriptor - Descriptor of the buffer containing (or to contain) the
+// token. If not specified, TokenBuffer and TokenSize will be returned
+// as NULL.
+//
+// BufferIndex - Index of the token buffer to find (0 for first, 1 for
+// second).
+//
+// TokenBuffer - Returns a pointer to the buffer for the token.
+//
+// TokenSize - Returns a pointer to the location of the size of the buffer.
+//
+// ReadonlyOK - TRUE if the token buffer may be readonly.
+//
+// Requires:
+//
+// Returns:
+//
+// TRUE - If token buffer was properly found.
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+BOOLEAN
+GetTokenBuffer(
+ IN PSecBufferDesc TokenDescriptor OPTIONAL,
+ IN ULONG BufferIndex,
+ OUT PVOID * TokenBuffer,
+ OUT PULONG TokenSize,
+ IN BOOLEAN ReadonlyOK
+ )
+{
+ ULONG i, Index = 0;
+
+ PAGED_CODE();
+
+ //
+ // If there is no TokenDescriptor passed in,
+ // just pass out NULL to our caller.
+ //
+
+ if ( !ARGUMENT_PRESENT( TokenDescriptor) ) {
+ *TokenBuffer = NULL;
+ *TokenSize = 0;
+ return TRUE;
+ }
+
+ //
+ // Check the version of the descriptor.
+ //
+
+ if ( TokenDescriptor->ulVersion != SECBUFFER_VERSION ) {
+ return FALSE;
+ }
+
+ //
+ // Loop through each described buffer.
+ //
+
+ for ( i=0; i<TokenDescriptor->cBuffers ; i++ ) {
+ PSecBuffer Buffer = &TokenDescriptor->pBuffers[i];
+ if ( (Buffer->BufferType & (~SECBUFFER_READONLY)) == SECBUFFER_TOKEN ) {
+
+ //
+ // If the buffer is readonly and readonly isn't OK,
+ // reject the buffer.
+ //
+
+ if ( !ReadonlyOK && (Buffer->BufferType & SECBUFFER_READONLY) ) {
+ return FALSE;
+ }
+
+ if (Index != BufferIndex)
+ {
+ Index++;
+ continue;
+ }
+
+ //
+ // Return the requested information
+ //
+
+ *TokenBuffer = Buffer->pvBuffer;
+ *TokenSize = Buffer->cbBuffer;
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: GetSecurityToken
+//
+// Synopsis:
+// This routine parses a Token Descriptor and pulls out the useful
+// information.
+//
+// Effects:
+//
+// Arguments:
+// TokenDescriptor - Descriptor of the buffer containing (or to contain) the
+// token. If not specified, TokenBuffer and TokenSize will be returned
+// as NULL.
+//
+// BufferIndex - Index of the token buffer to find (0 for first, 1 for
+// second).
+//
+// TokenBuffer - Returns a pointer to the buffer for the token.
+//
+// Requires:
+//
+// Returns:
+//
+// TRUE - If token buffer was properly found.
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+BOOLEAN
+GetSecurityToken(
+ IN PSecBufferDesc TokenDescriptor OPTIONAL,
+ IN ULONG BufferIndex,
+ OUT PSecBuffer * TokenBuffer
+ )
+{
+ ULONG i;
+ ULONG Index = 0;
+
+ PAGED_CODE();
+
+ //
+ // If there is no TokenDescriptor passed in,
+ // just pass out NULL to our caller.
+ //
+
+ if ( !ARGUMENT_PRESENT( TokenDescriptor) ) {
+ *TokenBuffer = NULL;
+ return TRUE;
+ }
+
+ //
+ // Check the version of the descriptor.
+ //
+
+ if ( TokenDescriptor->ulVersion != SECBUFFER_VERSION ) {
+ return FALSE;
+ }
+
+ //
+ // Loop through each described buffer.
+ //
+
+ for ( i=0; i<TokenDescriptor->cBuffers ; i++ ) {
+ PSecBuffer Buffer = &TokenDescriptor->pBuffers[i];
+ if ( (Buffer->BufferType & (~SECBUFFER_READONLY)) == SECBUFFER_TOKEN ) {
+
+ //
+ // If the buffer is readonly and readonly isn't OK,
+ // reject the buffer.
+ //
+
+ if ( Buffer->BufferType & SECBUFFER_READONLY ) {
+ return FALSE;
+ }
+
+ if (Index != BufferIndex)
+ {
+ Index++;
+ continue;
+ }
+ //
+ // Return the requested information
+ //
+
+ *TokenBuffer = Buffer;
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
diff --git a/private/lsa/client/ssp/userstub.c b/private/lsa/client/ssp/userstub.c
new file mode 100644
index 000000000..f13eaf52c
--- /dev/null
+++ b/private/lsa/client/ssp/userstub.c
@@ -0,0 +1,460 @@
+//+-----------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (c) Microsoft Corporation 1992 - 1994
+//
+// File: userstub.cxx
+//
+// Contents: stubs for user-mode security APIs
+//
+//
+// History: 3-7-94 MikeSw Created
+//
+//------------------------------------------------------------------------
+
+#include <sspdrv.h>
+
+#pragma alloc_text(PAGE, InitializePackages)
+#pragma alloc_text(PAGE, CompleteAuthToken)
+#pragma alloc_text(PAGE, ImpersonateSecurityContext)
+#pragma alloc_text(PAGE, QuerySecurityContextToken)
+#pragma alloc_text(PAGE, RevertSecurityContext)
+#pragma alloc_text(PAGE, QueryContextAttributesW)
+#pragma alloc_text(PAGE, MakeSignature)
+#pragma alloc_text(PAGE, VerifySignature)
+#pragma alloc_text(PAGE, SealMessage)
+#pragma alloc_text(PAGE, UnsealMessage)
+
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: InitializePackages
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS
+InitializePackages(void)
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+
+ scRet = NtlmInitialize();
+ if (!NT_SUCCESS(scRet))
+ {
+ DebugLog((DEB_ERROR,"Failed to initialize package\n"));
+ }
+ return(scRet);
+}
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: CompleteAuthToken
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+CompleteAuthToken(
+ PCtxtHandle phContext, // Context to complete
+ PSecBufferDesc pToken // Token to complete
+ )
+{
+ PAGED_CODE();
+ return(SEC_E_UNSUPPORTED_FUNCTION);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: ImpersonateSecurityContext
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+ImpersonateSecurityContext(
+ PCtxtHandle phContext // Context to impersonate
+ )
+{
+ SECURITY_STATUS scRet;
+ PACCESS_TOKEN Token;
+
+ PAGED_CODE();
+
+ scRet = NtlmGetToken(phContext->dwLower,NULL,&Token);
+ if (NT_SUCCESS(scRet))
+ {
+ if (Token == NULL)
+ {
+ scRet = SEC_E_NO_IMPERSONATION;
+ }
+ else
+ {
+// scRet = ObReferenceObjectByPointer(
+// Token,
+// TOKEN_IMPERSONATE,
+// NULL, // no object type
+// KeGetPreviousMode()
+// );
+// if (NT_SUCCESS(scRet))
+ {
+ PsImpersonateClient(
+ PsGetCurrentThread(),
+ Token,
+ FALSE,
+ FALSE,
+ SeTokenImpersonationLevel(Token)
+ );
+ }
+ }
+ }
+
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: RevertSecurityContext
+//
+// Synopsis: Revert the thread to the process identity
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS SEC_ENTRY
+RevertSecurityContext(
+ PCtxtHandle phContext // Context from which to re
+ )
+{
+ SECURITY_IMPERSONATION_LEVEL Unused = SecurityImpersonation;
+ PAGED_CODE();
+
+ PsImpersonateClient(
+ PsGetCurrentThread(),
+ NULL,
+ FALSE,
+ FALSE,
+ Unused
+ );
+
+
+ return(STATUS_SUCCESS);
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: QuerySecurityContextToken
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+QuerySecurityContextToken(
+ PCtxtHandle phContext,
+ PHANDLE TokenHandle
+ )
+{
+ SECURITY_STATUS scRet;
+ HANDLE hToken;
+
+ PAGED_CODE();
+
+ scRet = NtlmGetToken(phContext->dwLower,&hToken, NULL);
+ if (NT_SUCCESS(scRet))
+ {
+ if (hToken != NULL)
+ {
+ //
+ // Duplicate the token so the caller may hold onto it after
+ // deleting the context
+ //
+
+ scRet = NtDuplicateObject(
+ NtCurrentProcess(),
+ hToken,
+ NtCurrentProcess(),
+ TokenHandle,
+ 0, // desired access
+ 0, // handle attributes
+ DUPLICATE_SAME_ACCESS
+ );
+ }
+ else
+ {
+ scRet = SEC_E_NO_IMPERSONATION;
+ }
+ }
+
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: QueryContextAttributesW
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments:
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//
+//--------------------------------------------------------------------------
+
+
+
+SECURITY_STATUS SEC_ENTRY
+QueryContextAttributesW(
+ PCtxtHandle phContext, // Context to query
+ unsigned long ulAttribute, // Attribute to query
+ void SEC_FAR * pBuffer // Buffer for attributes
+ )
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+
+ scRet = NtlmQueryAttributes(
+ phContext->dwLower,
+ ulAttribute,
+ pBuffer);
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: MakeSignature
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments: [phContext] -- context to use
+// [fQOP] -- quality of protection to use
+// [pMessage] -- message
+// [MessageSeqNo] -- sequence number of message
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS SEC_ENTRY
+MakeSignature( PCtxtHandle phContext,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo)
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+ scRet = NtlmMakeSignature(
+ phContext->dwLower,
+ fQOP,
+ pMessage,
+ MessageSeqNo);
+ return(scRet);
+}
+
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: VerifySignature
+//
+// Synopsis:
+//
+// Effects:
+//
+// Arguments: [phContext] -- Context performing the unseal
+// [pMessage] -- Message to verify
+// [MessageSeqNo] -- Sequence number of this message
+// [pfQOPUsed] -- quality of protection used
+//
+// Requires:
+//
+// Returns:
+//
+// Notes:
+//
+//--------------------------------------------------------------------------
+SECURITY_STATUS SEC_ENTRY
+VerifySignature(PCtxtHandle phContext,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOP)
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+
+ scRet = NtlmVerifySignature(
+ phContext->dwLower,
+ pMessage,
+ MessageSeqNo,
+ pfQOP);
+ return(scRet);
+
+}
+
+//+---------------------------------------------------------------------------
+//
+// Function: SealMessage
+//
+// Synopsis: Seals a message
+//
+// Effects:
+//
+// Arguments: [phContext] -- context to use
+// [fQOP] -- quality of protection to use
+// [pMessage] -- message
+// [MessageSeqNo] -- sequence number of message
+//
+// History: 5-06-93 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+SealMessage( PCtxtHandle phContext,
+ ULONG fQOP,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo)
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+
+ scRet = NtlmSealMessage(
+ phContext->dwLower,
+ fQOP,
+ pMessage,
+ MessageSeqNo);
+ return(scRet);
+
+
+}
+
+//+---------------------------------------------------------------------------
+//
+// Function: UnsealMessage
+//
+// Synopsis: Unseal a private message
+//
+// Arguments: [phContext] -- Context performing the unseal
+// [pMessage] -- Message to unseal
+// [MessageSeqNo] -- Sequence number of this message
+// [pfQOPUsed] -- quality of protection used
+//
+// History: 5-06-93 RichardW Created
+//
+// Notes:
+//
+//----------------------------------------------------------------------------
+
+
+SECURITY_STATUS SEC_ENTRY
+UnsealMessage( PCtxtHandle phContext,
+ PSecBufferDesc pMessage,
+ ULONG MessageSeqNo,
+ ULONG * pfQOP)
+{
+ SECURITY_STATUS scRet;
+
+ PAGED_CODE();
+
+ scRet = NtlmUnsealMessage(
+ phContext->dwLower,
+ pMessage,
+ MessageSeqNo,
+ pfQOP);
+ return(scRet);
+
+}
+
+
+
+
+
diff --git a/private/lsa/client/user/makefile b/private/lsa/client/user/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/client/user/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/client/user/sources b/private/lsa/client/user/sources
new file mode 100644
index 000000000..6a44426aa
--- /dev/null
+++ b/private/lsa/client/user/sources
@@ -0,0 +1,36 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=client
+
+TARGETNAME=lsadll
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\inc
+
+SOURCES= austub.c
+
+
diff --git a/private/lsa/common/cr.c b/private/lsa/common/cr.c
new file mode 100644
index 000000000..5ad467f99
--- /dev/null
+++ b/private/lsa/common/cr.c
@@ -0,0 +1,403 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ cr.c
+
+Abstract:
+
+ Local Security Authority - Cipher Routines common to Client and Server
+
+ These routines interface the LSA client or server sides with the Cipher
+ Routines. They perform RPC-style memory allocation.
+
+Author:
+
+ Scott Birrell (ScottBi) December 13, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <lsacomp.h>
+
+VOID
+LsapCrFreeMemoryValue(
+ IN PVOID MemoryValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function frees the memory allocated for an Cipher Value.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ //
+ // The memory is currently a counted string contained in a UNICODE
+ // STRING structure in which the buffer follows the structure. A
+ // single MIDL_user_free will therefore do the trick.
+ //
+
+ MIDL_user_free(MemoryValue);
+}
+
+
+NTSTATUS
+LsapCrEncryptValue(
+ IN OPTIONAL PLSAP_CR_CLEAR_VALUE ClearValue,
+ IN PLSAP_CR_CIPHER_KEY CipherKey,
+ OUT PLSAP_CR_CIPHER_VALUE *CipherValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function two-way encrypts a Value with the given Cipher Key
+ and allocates memory for the output. The memory must be freed after
+ use by calling LsapCrFreeMemoryValue().
+
+Arguments:
+
+ ClearValue - Pointer to structure referencing value to be encrypted.
+ A NULL pointer may be specified.
+
+ CipherKey - Pointer to structure referencing the Cipher Key
+
+ CipherValue - Receives a pointer to a structure referencing the
+ encrypted value or NULL.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_CR_CIPHER_VALUE TempCipherValue;
+ PLSAP_CR_CIPHER_VALUE OutputCipherValue = NULL;
+ ULONG CipherValueBufferLength;
+
+ //
+ // If NULL is specified for input, return NULL for output.
+ //
+
+ if (!ARGUMENT_PRESENT(ClearValue)) {
+
+ *CipherValue = NULL;
+ }
+
+ //
+ // Obtain the length of the encrypted value buffer that will be
+ // required by calling the encryption routine in 'query' mode
+ // by passing a pointer to a return Cipher Value structure containing
+ // a MaximumLength of 0.
+ //
+
+ TempCipherValue.MaximumLength = 0;
+ TempCipherValue.Length = 0;
+ TempCipherValue.Buffer = NULL;
+
+ Status = LsapCrRtlEncryptData(
+ ClearValue,
+ CipherKey,
+ &TempCipherValue
+ );
+
+ if (Status != STATUS_BUFFER_TOO_SMALL) {
+
+ goto EncryptValueError;
+ }
+
+ //
+ // Allocate memory for the output structure followed by buffer.
+ //
+
+ CipherValueBufferLength = TempCipherValue.Length;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ OutputCipherValue = MIDL_user_allocate(
+ sizeof (LSAP_CR_CIPHER_VALUE) +
+ CipherValueBufferLength
+ );
+
+ if (OutputCipherValue == NULL) {
+
+ goto EncryptValueError;
+ }
+
+ //
+ // Initialize Cipher Value structure. The Buffer pointer is set to
+ // to point to the byte following the structure header.
+ //
+
+ OutputCipherValue->Buffer = (PCHAR)(OutputCipherValue + 1);
+ OutputCipherValue->MaximumLength = CipherValueBufferLength;
+ OutputCipherValue->Length = CipherValueBufferLength;
+
+ //
+ // Now call the two-way encryption routine.
+ //
+
+ Status = LsapCrRtlEncryptData(
+ ClearValue,
+ CipherKey,
+ OutputCipherValue
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ *CipherValue = OutputCipherValue;
+ return(Status);
+ }
+
+EncryptValueError:
+
+ //
+ // If necessary, free the memory allocated for the output encrypted value.
+ //
+
+ if (OutputCipherValue != NULL) {
+
+ MIDL_user_free(OutputCipherValue);
+ }
+
+ *CipherValue = NULL;
+ return(Status);
+}
+
+
+NTSTATUS
+LsapCrDecryptValue(
+ IN OPTIONAL PLSAP_CR_CIPHER_VALUE CipherValue,
+ IN PLSAP_CR_CIPHER_KEY CipherKey,
+ OUT PLSAP_CR_CLEAR_VALUE *ClearValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function decrypts a Value that has been two-way Cipher with the
+ given Cipher Key and allocates memory for the output. The memory
+ must be freed after use by calling LsapCrFreeMemoryValue();
+
+Arguments:
+
+ CipherValue - Pointer to structure referencing encrypted Value.
+
+ CipherKey - Pointer to structure referencing the Cipher Key
+
+ ClearValue - Receives a pointer to a structure referencing the
+ Decrypted Value.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_CR_CLEAR_VALUE TempClearValue;
+ PLSAP_CR_CLEAR_VALUE OutputClearValue = NULL;
+ ULONG ClearValueBufferLength;
+
+ //
+ // If NULL is specified for input, return NULL for output.
+ //
+
+ if (!ARGUMENT_PRESENT(CipherValue)) {
+
+ *ClearValue = NULL;
+ }
+
+ //
+ // Obtain the length of the decrypted (clear) value buffer that will be
+ // required by calling the decryption routine in 'query' mode
+ // by passing a pointer to a return Clear Value structure containing
+ // a MaximumLength of 0.
+ //
+
+ TempClearValue.MaximumLength = 0;
+ TempClearValue.Length = 0;
+ TempClearValue.Buffer = NULL;
+
+ Status = LsapCrRtlDecryptData(
+ CipherValue,
+ CipherKey,
+ &TempClearValue
+ );
+
+ //
+ // Since we supplied a buffer length of 0, we would normally expect
+ // to receive STATUS_BUFFER_TOO_SMALL back plus the buffer size required.
+ // There is one exceptional case and that is where the original
+ // unencrypted data had length 0. In this case, we expect
+ // STATUS_SUCCESS and a length required equal to 0 returned.
+ //
+
+ if (Status != STATUS_BUFFER_TOO_SMALL) {
+
+ if (!(Status == STATUS_SUCCESS && TempClearValue.Length == 0)) {
+ goto DecryptValueError;
+ }
+ }
+
+ //
+ // Allocate memory for the output structure followed by buffer.
+ //
+
+ ClearValueBufferLength = TempClearValue.Length;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ OutputClearValue = MIDL_user_allocate(
+ sizeof (LSAP_CR_CLEAR_VALUE) +
+ ClearValueBufferLength
+ );
+
+ if (OutputClearValue == NULL) {
+
+ goto DecryptValueError;
+ }
+
+ //
+ // Initialize Clear Value structure. The Buffer pointer is set to
+ // to point to the byte following the structure header.
+ //
+
+ OutputClearValue->Buffer = (PCHAR)(OutputClearValue + 1);
+ OutputClearValue->MaximumLength = ClearValueBufferLength;
+ OutputClearValue->Length = ClearValueBufferLength;
+
+ //
+ // Now call the two-way decryption routine.
+ //
+
+ Status = LsapCrRtlDecryptData(
+ CipherValue,
+ CipherKey,
+ OutputClearValue
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ *ClearValue = OutputClearValue;
+ return(Status);
+ }
+
+DecryptValueError:
+
+ //
+ // If necessary, free the memory allocated for the output decrypted value.
+ //
+
+ if (OutputClearValue != NULL) {
+
+ MIDL_user_free(OutputClearValue);
+ }
+
+ *ClearValue = NULL;
+ return(Status);
+}
+
+
+VOID
+LsapCrUnicodeToClearValue(
+ IN OPTIONAL PUNICODE_STRING UnicodeString,
+ OUT PLSAP_CR_CLEAR_VALUE ClearValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a Unicode structure to a Clear Value structure.
+
+Arguments:
+
+ UnicodeString - Optional pointer to Unicode string. If NULL, the
+ output Clear Value structure is initialized to have zero
+ length and Maximum length, and with a NULL buffer pointer.
+
+ ClearValue - Pointer to Clear Value structure.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ UNICODE_STRING IntermediateUnicodeString;
+
+ if (ARGUMENT_PRESENT(UnicodeString)) {
+
+ IntermediateUnicodeString = *UnicodeString;
+
+ ClearValue->Length = (ULONG) IntermediateUnicodeString.Length;
+ ClearValue->MaximumLength = (ULONG) IntermediateUnicodeString.MaximumLength;
+ ClearValue->Buffer = (PUCHAR) IntermediateUnicodeString.Buffer;
+ return;
+ }
+
+ ClearValue->Length = ClearValue->MaximumLength = 0;
+ ClearValue->Buffer = NULL;
+}
+
+
+VOID
+LsapCrClearValueToUnicode(
+ IN OPTIONAL PLSAP_CR_CLEAR_VALUE ClearValue,
+ OUT PUNICODE_STRING UnicodeString
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a Clear Value to a Unicode String. The Clear
+ Value structure must have valid syntax - no checking will be done.
+
+
+Arguments:
+
+ ClearValue - Optional pointer to Clear Value to be converted. If
+ NULL is specified, the output Unicode String structure will
+ be initialized to point to the NULL string.
+
+ UnicodeString - Pointer to target Unicode String structure.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LSAP_CR_CLEAR_VALUE IntermediateClearValue;
+
+ if (ARGUMENT_PRESENT(ClearValue)) {
+
+ IntermediateClearValue = *ClearValue;
+
+ UnicodeString->Length = (USHORT) IntermediateClearValue.Length;
+ UnicodeString->MaximumLength = (USHORT) IntermediateClearValue.MaximumLength;
+ UnicodeString->Buffer = (PWSTR) IntermediateClearValue.Buffer;
+ return;
+ }
+
+ UnicodeString->Length = UnicodeString->MaximumLength = 0;
+ UnicodeString->Buffer = NULL;
+}
diff --git a/private/lsa/common/lsaprtl.c b/private/lsa/common/lsaprtl.c
new file mode 100644
index 000000000..e5121838c
--- /dev/null
+++ b/private/lsa/common/lsaprtl.c
@@ -0,0 +1,1158 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsaprtl.c
+
+Abstract:
+
+ Local Security Authority - Temporary Rtl Routine Definitions.
+
+ This file contains routines used in the LSA that could be made into Rtl
+ routines. They have been written in general purpose form with this in
+ mind - the only exception to thisa is that their names have Lsap prefixes
+ to indicate that they are currently used only by the LSA.
+
+Author:
+
+ Scott Birrell (ScottBi) April 8, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <lsacomp.h>
+
+NTSTATUS
+LsapRtlConvertSidToUnicodeString(
+ IN PSID Sid,
+ OUT PUNICODE_STRING UnicodeString
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a Sid to Text format.
+
+Arguments:
+
+ Sid - Pointer to Sid.
+
+ UnicodeString - Pointer to Output Unicode String.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory, to complete the call.
+
+WARNING! This is a temporary hack.
+
+--*/
+
+{
+ NTSTATUS Status;
+ UCHAR Buffer[256];
+ UCHAR String[256];
+
+ UCHAR i;
+ ULONG Tmp;
+
+ PISID iSid = (PISID)Sid; // pointer to opaque structure
+
+ ANSI_STRING AnsiString;
+
+ sprintf(Buffer, "S-%u-", (USHORT)iSid->Revision );
+ lstrcpy(String, Buffer);
+
+ if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
+ (iSid->IdentifierAuthority.Value[1] != 0) ){
+ sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)iSid->IdentifierAuthority.Value[0],
+ (USHORT)iSid->IdentifierAuthority.Value[1],
+ (USHORT)iSid->IdentifierAuthority.Value[2],
+ (USHORT)iSid->IdentifierAuthority.Value[3],
+ (USHORT)iSid->IdentifierAuthority.Value[4],
+ (USHORT)iSid->IdentifierAuthority.Value[5] );
+ lstrcat(String, Buffer);
+
+ } else {
+
+ Tmp = (ULONG)iSid->IdentifierAuthority.Value[5] +
+ (ULONG)(iSid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(iSid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
+ sprintf(Buffer, "%lu", Tmp);
+ lstrcat(String, Buffer);
+ }
+
+ for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
+ sprintf(Buffer, "-%lu", iSid->SubAuthority[i]);
+ lstrcat(String, Buffer);
+ }
+
+ //
+ // Convert the string to a Unicode String
+ //
+
+ RtlInitString(&AnsiString, (PSZ) String);
+
+ Status = RtlAnsiStringToUnicodeString( UnicodeString, &AnsiString, TRUE);
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapRtlCopyUnicodeString(
+ OUT PUNICODE_STRING OutputString,
+ IN PUNICODE_STRING InputString,
+ BOOLEAN AllocateMemory
+ )
+
+/*++
+
+Routine Description:
+
+ This function copies one Unicode String to another, optionally allocating
+ memory for the output string buffer.
+
+Arguments:
+
+ OutputString - Pointer to Unicode String structure that will be made
+ to reference the output string.
+
+ InputString - Pointer to Unicode String structure to be copied.
+
+ AllocateMemory - Allocate memory for output string.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ (e.g. memory) to complete the call.
+--*/
+
+{
+ ANSI_STRING IntermediateAnsiString;
+ NTSTATUS Status;
+
+ //
+ // If we're to make a fresh copy of the string buffer, do so.
+ //
+
+ if (AllocateMemory) {
+
+ //
+ // A fresh copy is needed or memory not allocated yet for
+ // output string. Translate string back to Ansi then back
+ // to Unicode (sorry about the algorithm).
+ //
+
+ Status = RtlUnicodeStringToAnsiString(
+ &IntermediateAnsiString,
+ InputString,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = RtlAnsiStringToUnicodeString(
+ OutputString,
+ &IntermediateAnsiString,
+ TRUE
+ );
+ } else {
+
+ //
+ // No memory allocation required. Just copy the string structures.
+ //
+
+ *OutputString = *InputString;
+
+ return(STATUS_SUCCESS);
+ }
+
+ RtlFreeAnsiString(&IntermediateAnsiString);
+
+ return(Status);
+}
+
+
+BOOLEAN
+LsapRtlPrefixSid(
+ IN PSID PrefixSid,
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if one Sid is the Prefix Sid of another.
+
+Arguments:
+
+ PrefixSid - Pointer to Prefix Sid.
+
+ Sid - Pointer to Sid to be checked.
+
+Return Values:
+
+ BOOLEAN - TRUE if PrefixSid is the Prefix Sid of Sid, else FALSE.
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = FALSE;
+
+ if ((*RtlSubAuthorityCountSid(Sid)) > 0) {
+
+ //
+ // Decrement the SubAuthorityCount of Sid temporarily.
+ //
+
+ (*RtlSubAuthorityCountSid(Sid))--;
+
+ //
+ // Compare the Prefix Sid with the modified Sid.
+ //
+
+ BooleanStatus = RtlEqualSid( PrefixSid, Sid);
+
+ //
+ // Restore the original SubAuthorityCount.
+ //
+
+ (*RtlSubAuthorityCountSid(Sid))++;
+ }
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+LsapRtlPrefixName(
+ IN PUNICODE_STRING PrefixName,
+ IN PUNICODE_STRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if a Name has the given name as a Prefix
+
+Arguments:
+
+ PrefixName - Pointer to Prefix Name.
+
+ Name - Pointer to Name to be checked.
+
+Return Values:
+
+ BOOLEAN - TRUE if the Name is composite (i.e. contains a "\") and
+ PrefixName is the Prefix part of Name, else FALSE.
+
+--*/
+
+{
+ UNICODE_STRING TruncatedName = *Name;
+
+ if ((PrefixName->Length < Name->Length) &&
+ Name->Buffer[PrefixName->Length / 2] == *L"\\") {
+
+ TruncatedName.Length = PrefixName->Length;
+
+ if (RtlEqualUnicodeString(PrefixName, &TruncatedName, FALSE)) {
+
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+
+
+VOID
+LsapRtlSplitNames(
+ IN PUNICODE_STRING Names,
+ IN ULONG Count,
+ IN PUNICODE_STRING Separator,
+ OUT PUNICODE_STRING PrefixNames,
+ OUT PUNICODE_STRING SuffixNames
+ )
+
+/*++
+
+Routine Description:
+
+ This function splits an array of Names into Prefix and Suffix parts
+ separated by the given separator. The input array may contain names of
+ the following form:
+
+ <SuffixName>
+ <PrefixName> "\" <SuffixName>
+ The NULL string
+
+ Note that the output arrays will reference the original name strings.
+ No copying is done.
+
+Arguments:
+
+ Names - Pointer to array of Unicode Names.
+
+ Count - Count of Names in Names.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ that will be initialized to point to the Prefix portions of the
+ Names.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ that will be initialized to point to the Suffix portions of the
+ Names.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ULONG Index;
+ LONG SeparatorOffset;
+ LONG WideSeparatorOffset;
+
+ //
+ // Scan each name, initializing the output Unicode structures.
+ //
+
+ for (Index = 0; Index < Count; Index++) {
+
+ PrefixNames[Index] = Names[Index];
+ SuffixNames[Index] = Names[Index];
+
+ //
+ // Locate the separator "\" if any.
+ //
+
+ SeparatorOffset = LsapRtlFindCharacterInUnicodeString(
+ &Names[Index],
+ Separator,
+ FALSE
+ );
+
+ //
+ // If there is a separator, make the Prefix Name point to the
+ // part of the name before the separator and make the Suffix Name
+ // point to the part of the name after the separator. If there
+ // is no separator, set the Prefix Name part to Null. Rememeber
+ // that the Length fields are byte counts, not Wide Character
+ // counts.
+ //
+
+ if (SeparatorOffset >= 0) {
+
+ WideSeparatorOffset = (SeparatorOffset / sizeof(WCHAR));
+ PrefixNames[Index].Length = (USHORT) SeparatorOffset;
+ SuffixNames[Index].Buffer += (WideSeparatorOffset + 1);
+ SuffixNames[Index].Length -= (USHORT)(SeparatorOffset + sizeof(WCHAR));
+
+ } else {
+
+ WideSeparatorOffset = SeparatorOffset;
+ PrefixNames[Index].Length = 0;
+ }
+
+ //
+ // Set MaximumLengths equal to Lengths and, for safety, clear buffer
+ // pointers(s) to NULL in output strings if Length(s) are 0.
+ //
+
+ PrefixNames[Index].MaximumLength = PrefixNames[Index].Length;
+ SuffixNames[Index].MaximumLength = SuffixNames[Index].Length;
+
+ if (PrefixNames[Index].Length == 0) {
+
+ PrefixNames[Index].Buffer = NULL;
+ }
+
+ if (SuffixNames[Index].Length == 0) {
+
+ SuffixNames[Index].Buffer = NULL;
+ }
+ }
+}
+
+
+LONG
+LsapRtlFindCharacterInUnicodeString(
+ IN PUNICODE_STRING InputString,
+ IN PUNICODE_STRING Character,
+ IN BOOLEAN CaseInsensitive
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the byte offset of the first occurrence (if any) of
+ a Unicode Character within a Unicode String.
+
+Arguments
+
+ InputString - Pointer to Unicode String to be searched.
+
+ Character - Pointer to Unicode String initialized to character
+ to be searched for.
+
+ CaseInsensitive - TRUE if case is to be ignored, else FALSE.
+ NOTE - Only FALSE is supported just now.
+
+Return Value:
+
+ LONG - If the character is present within the string, its non-negative
+ byte offset is returned. If the character is not present within
+ the string, a negative value is returned.
+
+--*/
+
+{
+ BOOLEAN CharacterFound = FALSE;
+ ULONG Offset;
+
+ if (!CaseInsensitive) {
+
+ Offset = 0;
+
+ while (Offset < InputString->Length) {
+
+ if (*(Character->Buffer) ==
+ InputString->Buffer[Offset / sizeof (WCHAR)]) {
+
+ CharacterFound = TRUE;
+ break;
+ }
+
+ Offset += 2;
+ }
+
+ } else {
+
+ //
+ // Case Insensitive is not supported
+ //
+
+ CharacterFound = FALSE;
+ }
+
+ if (!CharacterFound) {
+
+ Offset = LSA_UNKNOWN_ID;
+ }
+
+ return(Offset);
+}
+
+
+VOID
+LsapRtlSetSecurityAccessMask(
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ NOTE! THIS ROUTINE IS IDENTICAL WITH SeSetSecurityAccessMask()
+ IN \nt\private\ntos\se\semethod.c
+
+ This routine builds an access mask representing the accesses necessary
+ to set the object security information specified in the SecurityInformation
+ parameter. While it is not difficult to determine this information,
+ the use of a single routine to generate it will ensure minimal impact
+ when the security information associated with an object is extended in
+ the future (to include mandatory access control information).
+
+Arguments:
+
+ SecurityInformation - Identifies the object's security information to be
+ modified.
+
+ DesiredAccess - Points to an access mask to be set to represent the
+ accesses necessary to modify the information specified in the
+ SecurityInformation parameter.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Figure out accesses needed to perform the indicated operation(s).
+ //
+
+ (*DesiredAccess) = 0;
+
+ if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
+ (SecurityInformation & GROUP_SECURITY_INFORMATION) ) {
+ (*DesiredAccess) |= WRITE_OWNER;
+ }
+
+ if (SecurityInformation & DACL_SECURITY_INFORMATION) {
+ (*DesiredAccess) |= WRITE_DAC;
+ }
+
+ if (SecurityInformation & SACL_SECURITY_INFORMATION) {
+ (*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ return;
+}
+
+
+VOID
+LsapRtlQuerySecurityAccessMask(
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ NOTE! THIS ROUTINE IS IDENTICAL WITH SeQuerySecurityAccessMask()
+ IN \nt\private\ntos\se\semethod.c.
+
+ This routine builds an access mask representing the accesses necessary
+ to query the object security information specified in the
+ SecurityInformation parameter. While it is not difficult to determine
+ this information, the use of a single routine to generate it will ensure
+ minimal impact when the security information associated with an object is
+ extended in the future (to include mandatory access control information).
+
+Arguments:
+
+ SecurityInformation - Identifies the object's security information to be
+ queried.
+
+ DesiredAccess - Points to an access mask to be set to represent the
+ accesses necessary to query the information specified in the
+ SecurityInformation parameter.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Figure out accesses needed to perform the indicated operation(s).
+ //
+
+ (*DesiredAccess) = 0;
+
+ if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
+ (SecurityInformation & GROUP_SECURITY_INFORMATION) ||
+ (SecurityInformation & DACL_SECURITY_INFORMATION)) {
+ (*DesiredAccess) |= READ_CONTROL;
+ }
+
+ if ((SecurityInformation & SACL_SECURITY_INFORMATION)) {
+ (*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ return;
+
+}
+
+
+NTSTATUS
+LsapRtlSidToUnicodeRid(
+ IN PSID Sid,
+ OUT PUNICODE_STRING UnicodeRid
+ )
+
+/*++
+
+Routine Description:
+
+ This function extracts the Relative Id (Rid) from a Sid and
+ converts it to a Unicode String. The Rid is extracted and converted
+ to an 8-digit Unicode Integer.
+
+Arguments:
+
+ Sid - Pointer to the Sid to be converted. It is the caller's
+ responsibility to ensure that the Sid has valid syntax.
+
+ UnicodeRid - Pointer to a Unicode String structure that will receive
+ the Rid in Unicode form. Note that memory for the string buffer
+ in this Unicode String will be allocated by this routine if
+ successful. The caller must free this memory after use by calling
+ RtlFreeUnicodeString.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Status code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to allocate buffer for Unicode String name.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG Rid;
+ UCHAR SubAuthorityCount;
+ UCHAR RidNameBufferAnsi[9];
+
+ ANSI_STRING CharacterSidAnsi;
+
+ //
+ // First, verify that the given Sid is valid
+ //
+
+ if (!RtlValidSid( Sid )) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Sid is valid. If however, the SubAuthorityCount is zero,
+ // we cannot have a Rid so return error.
+ //
+
+ SubAuthorityCount = ((PISID) Sid)->SubAuthorityCount;
+
+ if (SubAuthorityCount == 0) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Sid has at least one subauthority. Get the lowest subauthority
+ // (i.e. the Rid).
+ //
+
+ Rid = ((PISID) Sid)->SubAuthority[SubAuthorityCount - 1];
+
+ //
+ // Now convert the Rid to an 8-digit numeric character string
+ //
+
+ Status = RtlIntegerToChar( Rid, 16, -8, RidNameBufferAnsi );
+
+ //
+ // Need to add null terminator to string
+ //
+
+ RidNameBufferAnsi[8] = 0;
+
+ //
+ // Initialize an ANSI string structure with the converted name.
+ //
+
+ RtlInitString( &CharacterSidAnsi, RidNameBufferAnsi );
+
+ //
+ // Convert the ANSI string structure to Unicode form
+ //
+
+ Status = RtlAnsiStringToUnicodeString(
+ UnicodeRid,
+ &CharacterSidAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapRtlPrivilegeSetToLuidAndAttributes(
+ IN OPTIONAL PPRIVILEGE_SET PrivilegeSet,
+ OUT PULONG PrivilegeCount,
+ OUT PLUID_AND_ATTRIBUTES *LuidAndAttributes
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a Privilege Set to a Privilege Count and Luid and
+ Attributes array.
+
+Arguments:
+
+ PrivilegeSet - Pointer to Privilege Set to be converted. If NULL or a zero
+ length Privilege Set is specified, NULL is returned for the LUID and
+ attributes pointer, with a Privilege Count of 0.
+
+ PrivilegeCount - Receives the output Privilege Count
+
+ LuidAndAttributes - Receives pointer to Luid and Attributes array. If there
+ are no privileges, NULL is returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLUID_AND_ATTRIBUTES OutputLuidAndAttributes = NULL;
+ ULONG OutputPrivilegeCount = 0;
+ ULONG LuidAndAttributesLength;
+
+ if (PrivilegeSet != NULL) {
+
+ OutputPrivilegeCount = PrivilegeSet->PrivilegeCount;
+
+ if (OutputPrivilegeCount > 0) {
+
+ //
+ // Allocate space for the output LUID_AND_ATTRIBUTES array.
+ //
+
+ LuidAndAttributesLength = sizeof(LUID_AND_ATTRIBUTES) * OutputPrivilegeCount;
+
+
+ OutputLuidAndAttributes = MIDL_user_allocate( LuidAndAttributesLength );
+
+ Status = STATUS_NO_MEMORY;
+
+ if (OutputLuidAndAttributes == NULL) {
+
+ goto PrivilegeSetToLuidAndAttributesError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Copy the LUID and attributes from the input Privilege Set.
+ //
+
+ RtlCopyMemory(
+ OutputLuidAndAttributes,
+ PrivilegeSet->Privilege,
+ LuidAndAttributesLength
+ );
+ }
+ }
+
+ //
+ // Return LUID and Attributes array or NULL, plus Count.
+ //
+
+ *LuidAndAttributes = OutputLuidAndAttributes;
+ *PrivilegeCount = OutputPrivilegeCount;
+
+PrivilegeSetToLuidAndAttributesFinish:
+
+ return(Status);
+
+PrivilegeSetToLuidAndAttributesError:
+
+ goto PrivilegeSetToLuidAndAttributesFinish;
+}
+
+
+NTSTATUS
+LsapRtlWellKnownPrivilegeCheck(
+ IN PVOID ObjectHandle,
+ IN BOOLEAN ImpersonateClient,
+ IN ULONG PrivilegeId,
+ IN OPTIONAL PCLIENT_ID ClientId
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if the given well known privilege is enabled for an
+ impersonated client or for the current process.
+
+Arguments:
+
+ ImpersonateClient - If TRUE, impersonate the client. If FALSE, don't
+ impersonate the client (we may already be doing so).
+
+ PrivilegeId - Specifies the well known Privilege Id
+
+ ClientId - Specifies the client process/thread Id. If already
+ impersonating the client, or impersonation is requested, this
+ parameter should be omitted.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully and the client
+ is either trusted or has the necessary privilege enabled.
+
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ BOOLEAN PrivilegeHeld = FALSE;
+ HANDLE ClientThread = NULL, ClientProcess = NULL, ClientToken = NULL;
+ OBJECT_ATTRIBUTES NullAttributes;
+ PRIVILEGE_SET Privilege;
+ BOOLEAN ClientImpersonatedHere = FALSE;
+ UNICODE_STRING SubsystemName;
+
+ InitializeObjectAttributes( &NullAttributes, NULL, 0, NULL, NULL );
+
+ //
+ // If requested, impersonate the client.
+ //
+
+ if (ImpersonateClient) {
+
+ Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ goto WellKnownPrivilegeCheckError;
+ }
+
+ ClientImpersonatedHere = TRUE;
+ }
+
+ //
+ // If a client process other than ourself has been specified , open it
+ // for query information access.
+ //
+
+ if (ARGUMENT_PRESENT(ClientId)) {
+
+ if (ClientId->UniqueProcess != NtCurrentProcess()) {
+
+ Status = NtOpenProcess(
+ &ClientProcess,
+ PROCESS_QUERY_INFORMATION, // To open primary token
+ &NullAttributes,
+ ClientId
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ goto WellKnownPrivilegeCheckError;
+ }
+
+ } else {
+
+ ClientProcess = NtCurrentProcess();
+ }
+ }
+
+ //
+ // If a client thread other than ourself has been specified , open it
+ // for query information access.
+ //
+
+ if (ARGUMENT_PRESENT(ClientId)) {
+
+ if (ClientId->UniqueThread != NtCurrentThread()) {
+
+ Status = NtOpenThread(
+ &ClientThread,
+ THREAD_QUERY_INFORMATION,
+ &NullAttributes,
+ ClientId
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ goto WellKnownPrivilegeCheckError;
+ }
+
+ } else {
+
+ ClientThread = NtCurrentThread();
+ }
+
+ } else {
+
+ ClientThread = NtCurrentThread();
+ }
+
+ //
+ // Open the specified or current thread's impersonation token (if any).
+ //
+
+ Status = NtOpenThreadToken(
+ ClientThread,
+ TOKEN_QUERY,
+ TRUE,
+ &ClientToken
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ goto WellKnownPrivilegeCheckError;
+ }
+
+ //
+ // Make sure that we did not get any error in opening the impersonation
+ // token other than that the token doesn't exist.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if ( Status != STATUS_NO_TOKEN ) {
+
+ goto WellKnownPrivilegeCheckError;
+ }
+
+ //
+ // The thread isn't impersonating...open the process's token.
+ // A process Id must have been specified in the ClientId information
+ // in this case.
+ //
+
+ if (ClientProcess == NULL) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto WellKnownPrivilegeCheckError;
+ }
+
+ Status = NtOpenProcessToken(
+ ClientProcess,
+ TOKEN_QUERY,
+ &ClientToken
+ );
+
+ //
+ // Make sure we succeeded in opening the token
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ goto WellKnownPrivilegeCheckError;
+ }
+ }
+
+ //
+ // OK, we have a token open. Now check for the privilege to execute this
+ // service.
+ //
+
+ Privilege.PrivilegeCount = 1;
+ Privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ Privilege.Privilege[0].Luid = RtlConvertLongToLuid(PrivilegeId);
+ Privilege.Privilege[0].Attributes = 0;
+
+ Status = NtPrivilegeCheck(
+ ClientToken,
+ &Privilege,
+ &PrivilegeHeld
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto WellKnownPrivilegeCheckError;
+ }
+
+ RtlInitUnicodeString( &SubsystemName, L"LSA" );
+
+ (VOID) NtPrivilegeObjectAuditAlarm ( &SubsystemName,
+ ObjectHandle,
+ ClientToken,
+ ACCESS_SYSTEM_SECURITY,
+ &Privilege,
+ PrivilegeHeld
+ );
+ if ( !PrivilegeHeld ) {
+
+ Status = STATUS_PRIVILEGE_NOT_HELD;
+ goto WellKnownPrivilegeCheckError;
+ }
+
+WellKnownPrivilegeCheckFinish:
+
+ //
+ // If we impersonated the client, revert to ourself.
+ //
+
+ if (ClientImpersonatedHere) {
+
+ SecondaryStatus = I_RpcMapWin32Status(RpcRevertToSelf());
+ }
+
+ //
+ // If necessary, close the client Process.
+ //
+
+ if ((ARGUMENT_PRESENT(ClientId)) &&
+ (ClientId->UniqueProcess != NtCurrentProcess()) &&
+ (ClientProcess != NULL)) {
+
+ SecondaryStatus = NtClose( ClientProcess );
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+ ClientProcess = NULL;
+ }
+
+ //
+ // If necessary, close the client token.
+ //
+
+ if (ClientToken != NULL) {
+
+ SecondaryStatus = NtClose( ClientToken );
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+ ClientToken = NULL;
+ }
+
+ //
+ // If necessary, close the client thread
+ //
+
+ if ((ARGUMENT_PRESENT(ClientId)) &&
+ (ClientId->UniqueThread != NtCurrentThread()) &&
+ (ClientThread != NULL)) {
+
+ SecondaryStatus = NtClose( ClientThread );
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+ ClientThread = NULL;
+ }
+
+ return(Status);
+
+WellKnownPrivilegeCheckError:
+
+ goto WellKnownPrivilegeCheckFinish;
+}
+
+
+NTSTATUS
+LsapSplitSid(
+ IN PSID AccountSid,
+ IN OUT PSID *DomainSid,
+ OUT ULONG *Rid
+ )
+
+/*++
+
+Routine Description:
+
+ This function splits a sid into its domain sid and rid. The caller
+ can either provide a memory buffer for the returned DomainSid, or
+ request that one be allocated. If the caller provides a buffer, the buffer
+ is assumed to be of sufficient size. If allocated on the caller's behalf,
+ the buffer must be freed when no longer required via MIDL_user_free.
+
+Arguments:
+
+ AccountSid - Specifies the Sid to be split. The Sid is assumed to be
+ syntactically valid. Sids with zero subauthorities cannot be split.
+
+ DomainSid - Pointer to location containing either NULL or a pointer to
+ a buffer in which the Domain Sid will be returned. If NULL is
+ specified, memory will be allocated on behalf of the caller.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call successfully.
+
+ STATUS_INVALID_SID - The Sid is has a subauthority count of 0.
+--*/
+
+{
+ NTSTATUS NtStatus;
+ UCHAR AccountSubAuthorityCount;
+ ULONG AccountSidLength;
+
+ //
+ // Calculate the size of the domain sid
+ //
+
+ AccountSubAuthorityCount = *RtlSubAuthorityCountSid(AccountSid);
+
+
+ if (AccountSubAuthorityCount < 1) {
+
+ NtStatus = STATUS_INVALID_SID;
+ goto SplitSidError;
+ }
+
+ AccountSidLength = RtlLengthSid(AccountSid);
+
+ //
+ // If no buffer is required for the Domain Sid, we have to allocate one.
+ //
+
+ if (*DomainSid == NULL) {
+
+ //
+ // Allocate space for the domain sid (allocate the same size as the
+ // account sid so we can use RtlCopySid)
+ //
+
+ *DomainSid = MIDL_user_allocate(AccountSidLength);
+
+
+ if (*DomainSid == NULL) {
+
+ NtStatus = STATUS_INSUFFICIENT_RESOURCES;
+ goto SplitSidError;
+ }
+ }
+
+ //
+ // Copy the Account sid into the Domain sid
+ //
+
+ RtlMoveMemory(*DomainSid, AccountSid, AccountSidLength);
+
+ //
+ // Decrement the domain sid sub-authority count
+ //
+
+ (*RtlSubAuthorityCountSid(*DomainSid))--;
+
+ //
+ // Copy the rid out of the account sid
+ //
+
+ *Rid = *RtlSubAuthoritySid(AccountSid, AccountSubAuthorityCount-1);
+
+ NtStatus = STATUS_SUCCESS;
+
+SplitSidFinish:
+
+ return(NtStatus);
+
+SplitSidError:
+
+ goto SplitSidFinish;
+}
diff --git a/private/lsa/common/lsarpcmm.c b/private/lsa/common/lsarpcmm.c
new file mode 100644
index 000000000..b2cc92b0a
--- /dev/null
+++ b/private/lsa/common/lsarpcmm.c
@@ -0,0 +1,98 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsarpcmm.c
+
+Abstract:
+
+ LSA - Common Client/Server RPC Memory Management Routines
+
+Author:
+
+ Scott Birrell (ScottBi) April 8, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <lsacomp.h>
+
+
+PVOID
+MIDL_user_allocate (
+ unsigned int NumBytes
+ )
+
+/*++
+
+Routine Description:
+
+ Allocates storage for RPC server transactions. The RPC stubs will
+ either call MIDL_user_allocate when it needs to un-marshall data into a
+ buffer that the user must free. RPC servers will use MIDL_user_allocate to
+ allocate storage that the RPC server stub will free after marshalling
+ the data.
+
+Arguments:
+
+ NumBytes - The number of bytes to allocate.
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+
+{
+ PVOID Buffer = (PVOID) LocalAlloc(LMEM_FIXED,NumBytes);
+
+ if (Buffer != NULL) {
+
+ RtlZeroMemory( Buffer, NumBytes );
+ }
+
+ return( Buffer );
+}
+
+
+VOID
+MIDL_user_free (
+ void *MemPointer
+ )
+
+/*++
+
+Routine Description:
+
+ Frees storage used in RPC transactions. The RPC client can call this
+ function to free buffer space that was allocated by the RPC client
+ stub when un-marshalling data that is to be returned to the client.
+ The Client calls MIDL_user_free when it is finished with the data and
+ desires to free up the storage.
+ The RPC server stub calls MIDL_user_free when it has completed
+ marshalling server data that is to be passed back to the client.
+
+Arguments:
+
+ MemPointer - This points to the memory block that is to be released.
+
+Return Value:
+
+ none.
+
+Note:
+
+
+--*/
+
+{
+ LocalFree(MemPointer);
+}
diff --git a/private/lsa/common/makefile b/private/lsa/common/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/common/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/common/sources b/private/lsa/common/sources
new file mode 100644
index 000000000..faaec835c
--- /dev/null
+++ b/private/lsa/common/sources
@@ -0,0 +1,44 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=common
+
+TARGETNAME=lsacomm
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\inc;..\..\inc
+
+C_DEFINES=$(C_DEFINES) -D_ADVAPI32_
+
+
+
+SOURCES= \
+ cr.c \
+ lsarpcmm.c \
+ lsaprtl.c
+
+UMTYPE=windows
diff --git a/private/lsa/crypt/dirs b/private/lsa/crypt/dirs
new file mode 100644
index 000000000..32b66a117
--- /dev/null
+++ b/private/lsa/crypt/dirs
@@ -0,0 +1,26 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=engine \
+ dll
+
+OPTIONAL_DIRS= test
diff --git a/private/lsa/crypt/dll/block.c b/private/lsa/crypt/dll/block.c
new file mode 100644
index 000000000..ff6de542f
--- /dev/null
+++ b/private/lsa/crypt/dll/block.c
@@ -0,0 +1,235 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ block.c
+
+Abstract:
+
+ Block encryption functions implementation :
+
+ RtlEncryptBlock
+ RtlDecryptBlock
+ RtlEncrypStdBlock
+
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+#include <engine.h>
+
+#include <nturtl.h>
+
+RTL_CRITICAL_SECTION CriticalSection;
+
+
+BOOLEAN
+Sys003Initialize(
+ IN PVOID hmod,
+ IN ULONG Reason,
+ IN PCONTEXT Context
+ )
+{
+ NTSTATUS Status;
+
+ if (Reason == DLL_PROCESS_ATTACH) {
+ Status = RtlInitializeCriticalSection(&CriticalSection);
+ }
+
+ if (Reason == DLL_PROCESS_DETACH) {
+ Status = RtlDeleteCriticalSection(&CriticalSection);
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ return(Status == STATUS_SUCCESS);
+
+ DBG_UNREFERENCED_PARAMETER(hmod);
+ DBG_UNREFERENCED_PARAMETER(Context);
+}
+
+
+
+NTSTATUS
+RtlEncryptBlock(
+ IN PCLEAR_BLOCK ClearBlock,
+ IN PBLOCK_KEY BlockKey,
+ OUT PCYPHER_BLOCK CypherBlock
+ )
+
+/*++
+
+Routine Description:
+
+ Takes a block of data and encrypts it with a key producing
+ an encrypted block of data.
+
+Arguments:
+
+ ClearBlock - The block of data that is to be encrypted.
+
+ BlockKey - The key to use to encrypt data
+
+ CypherBlock - Encrypted data is returned here
+
+Return Values:
+
+ STATUS_SUCCESS - The data was encrypted successfully. The encrypted
+ data block is in CypherBlock
+
+ STATUS_UNSUCCESSFUL - Something failed. The CypherBlock is undefined.
+--*/
+
+{
+ unsigned Result;
+ NTSTATUS Status;
+
+ Status = RtlEnterCriticalSection(&CriticalSection);
+ ASSERT(NT_SUCCESS(Status));
+
+ Result = DES_ECB_LM(ENCR_KEY,
+ (const char *)BlockKey,
+ (unsigned char *)ClearBlock,
+ (unsigned char *)CypherBlock
+ );
+
+ Status = RtlLeaveCriticalSection(&CriticalSection);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ if (Result == CRYPT_OK) {
+ return(STATUS_SUCCESS);
+ } else {
+#if DBG
+ DbgPrint("EncryptBlock failed\n\r");
+#endif
+ return(STATUS_UNSUCCESSFUL);
+ }
+}
+
+
+
+
+NTSTATUS
+RtlDecryptBlock(
+ IN PCYPHER_BLOCK CypherBlock,
+ IN PBLOCK_KEY BlockKey,
+ OUT PCLEAR_BLOCK ClearBlock
+ )
+/*++
+
+Routine Description:
+
+ Takes a block of encrypted data and decrypts it with a key producing
+ the clear block of data.
+
+Arguments:
+
+ CypherBlock - The block of data to be decrypted
+
+ BlockKey - The key to use to decrypt data
+
+ ClearBlock - The decrpted block of data is returned here
+
+
+Return Values:
+
+ STATUS_SUCCESS - The data was decrypted successfully. The decrypted
+ data block is in ClearBlock
+
+ STATUS_UNSUCCESSFUL - Something failed. The ClearBlock is undefined.
+--*/
+
+{
+ unsigned Result;
+ NTSTATUS Status;
+
+ Status = RtlEnterCriticalSection(&CriticalSection);
+ ASSERT(NT_SUCCESS(Status));
+
+ Result = DES_ECB_LM(DECR_KEY,
+ (const char *)BlockKey,
+ (unsigned char *)CypherBlock,
+ (unsigned char *)ClearBlock
+ );
+
+ Status = RtlLeaveCriticalSection(&CriticalSection);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (Result == CRYPT_OK) {
+ return(STATUS_SUCCESS);
+ } else {
+#if DBG
+ DbgPrint("DecryptBlock failed\n\r");
+#endif
+ return(STATUS_UNSUCCESSFUL);
+ }
+}
+
+
+
+NTSTATUS
+RtlEncryptStdBlock(
+ IN PBLOCK_KEY BlockKey,
+ OUT PCYPHER_BLOCK CypherBlock
+ )
+
+/*++
+
+Routine Description:
+
+ Takes a block key encrypts the standard text block with it.
+ The resulting encrypted block is returned.
+ This is a One-Way-Function - the key cannot be recovered from the
+ encrypted data block.
+
+Arguments:
+
+ BlockKey - The key to use to encrypt the standard text block.
+
+ CypherBlock - The encrypted data is returned here
+
+Return Values:
+
+ STATUS_SUCCESS - The encryption was successful.
+ The result is in CypherBlock
+
+ STATUS_UNSUCCESSFUL - Something failed. The CypherBlock is undefined.
+--*/
+
+{
+ unsigned Result;
+ char StdEncrPwd[] = "KGS!@#$%";
+ NTSTATUS Status;
+
+ Status = RtlEnterCriticalSection(&CriticalSection);
+ ASSERT(NT_SUCCESS(Status));
+
+ Result = DES_ECB_LM(ENCR_KEY,
+ (const char *)BlockKey,
+ (unsigned char *)StdEncrPwd,
+ (unsigned char *)CypherBlock
+ );
+
+ Status = RtlLeaveCriticalSection(&CriticalSection);
+ ASSERT(NT_SUCCESS(Status));
+
+ if (Result == CRYPT_OK) {
+ return(STATUS_SUCCESS);
+ } else {
+#if DBG
+ DbgPrint("EncryptStd failed\n\r");
+#endif
+ return(STATUS_UNSUCCESSFUL);
+ }
+}
+
diff --git a/private/lsa/crypt/dll/data.c b/private/lsa/crypt/dll/data.c
new file mode 100644
index 000000000..304513c91
--- /dev/null
+++ b/private/lsa/crypt/dll/data.c
@@ -0,0 +1,762 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ data.c
+
+Abstract:
+
+ Arbitrary length data encryption functions implementation :
+
+ RtlEncryptData
+ RtlDecryptData
+
+
+Author:
+
+ David Chalmers (Davidc) 12-16-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+#include <engine.h>
+
+//
+// Version number of encrypted data
+// Update this number if the method used encrypt the data changes
+//
+#define DATA_ENCRYPTION_VERSION 1
+
+//
+// Private data types
+//
+typedef struct _CRYPTP_BUFFER {
+ ULONG Length; // Number of valid bytes in buffer
+ ULONG MaximumLength; // Number of bytes pointed to by buffer
+ PCHAR Buffer;
+ PCHAR Pointer; // Points into buffer
+} CRYPTP_BUFFER;
+typedef CRYPTP_BUFFER *PCRYPTP_BUFFER;
+
+//
+// Internal helper macros
+#define AdvanceCypherData(p) ((PCYPHER_BLOCK)(((PCRYPTP_BUFFER)p)->Pointer)) ++
+#define AdvanceClearData(p) ((PCLEAR_BLOCK)(((PCRYPTP_BUFFER)p)->Pointer)) ++
+
+
+
+//
+// Private routines
+//
+
+VOID
+InitializeBuffer(
+ OUT PCRYPTP_BUFFER PrivateBuffer,
+ IN PCRYPT_BUFFER PublicBuffer
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Copies fields from public buffer into private buffer.
+ Sets the Pointer field of the private buffer to the
+ base of the buffer.
+
+Arguments:
+
+ PrivateBuffer - out internal buffer we want to represent the public structure.
+
+ PublicBuffer - the buffer the caller passed us
+
+Return Values:
+
+ None
+--*/
+{
+ PrivateBuffer->Length = PublicBuffer->Length;
+ PrivateBuffer->MaximumLength = PublicBuffer->MaximumLength;
+ PrivateBuffer->Buffer = PublicBuffer->Buffer;
+ PrivateBuffer->Pointer = PublicBuffer->Buffer;
+}
+
+
+BOOLEAN
+ValidateDataKey(
+ IN PCRYPTP_BUFFER DataKey,
+ IN PBLOCK_KEY BlockKey
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Checks the validity of the data key and constructs a minimum length
+ key in the passed blockkey if the datakey is not long enough.
+
+Arguments:
+
+ DataKey - The data key
+
+Return Values:
+
+ TRUE if the key is valid, otherwise FALSE
+
+--*/
+{
+ if (DataKey->Length == 0) {
+ return(FALSE);
+ }
+
+ if (DataKey->Length < BLOCK_KEY_LENGTH) {
+
+ // Make up a minimum length key from the small data key we were
+ // given. Store it in the passed blockkey variable and point
+ // the datakey buffer at this temporary storage.
+
+ ULONG DataIndex, BlockIndex;
+
+ DataIndex = 0;
+ for (BlockIndex = 0; BlockIndex < BLOCK_KEY_LENGTH; BlockIndex ++) {
+ ((PCHAR)BlockKey)[BlockIndex] = DataKey->Buffer[DataIndex];
+ DataIndex ++;
+ if (DataIndex >= DataKey->Length) {
+ DataIndex = 0;
+ }
+ }
+
+ // Point the buffer at our constructed block key
+ DataKey->Buffer = (PCHAR)BlockKey;
+ DataKey->Pointer = (PCHAR)BlockKey;
+ DataKey->Length = BLOCK_KEY_LENGTH;
+ DataKey->MaximumLength = BLOCK_KEY_LENGTH;
+ }
+
+ return(TRUE);
+}
+
+
+VOID
+AdvanceDataKey(
+ IN PCRYPTP_BUFFER DataKey
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Moves the data key pointer on to point at the key to use to encrypt
+ the next data block. Wraps round at end of key data.
+
+Arguments:
+
+ DataKey - The data key
+
+Return Values:
+
+ STATUS_SUCCESS - No problems
+
+--*/
+{
+ if (DataKey->Length > BLOCK_KEY_LENGTH) {
+
+ PCHAR EndPointer;
+
+ // Advance pointer and wrap
+ DataKey->Pointer += BLOCK_KEY_LENGTH;
+ EndPointer = DataKey->Pointer + BLOCK_KEY_LENGTH;
+
+ if (EndPointer > &(DataKey->Buffer[DataKey->Length])) {
+
+ ULONG Overrun;
+
+ Overrun = EndPointer - &(DataKey->Buffer[DataKey->Length]);
+
+ DataKey->Pointer = DataKey->Buffer + (BLOCK_KEY_LENGTH - Overrun);
+ }
+ }
+}
+
+
+ULONG
+CalculateCypherDataLength(
+ IN PCRYPTP_BUFFER ClearData
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Returns the number of bytes required to encrypt the specified number
+ of clear data bytes.
+
+Arguments:
+
+ ClearData - The clear data
+
+Return Values:
+
+ Number of cypher bytes required.
+--*/
+{
+ ULONG CypherDataLength;
+ ULONG BlockExcess;
+
+ // We always store the length of the clear data as a whole block.
+ CypherDataLength = CYPHER_BLOCK_LENGTH + ClearData->Length;
+
+ // Round up to the next block
+ BlockExcess = CypherDataLength % CYPHER_BLOCK_LENGTH;
+ if (BlockExcess > 0) {
+ CypherDataLength += CYPHER_BLOCK_LENGTH - BlockExcess;
+ }
+
+ return(CypherDataLength);
+}
+
+
+NTSTATUS
+EncryptDataLength(
+ IN PCRYPTP_BUFFER Data,
+ IN PCRYPTP_BUFFER DataKey,
+ OUT PCRYPTP_BUFFER CypherData
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Encrypts the clear data length and puts the encrypted value in the
+ cypherdatabuffer. Advances the cypherdata buffer and datakey buffer pointers
+
+Arguments:
+
+ Data - The buffer whose length is to be encrypted
+
+ DataKey - key to use to encrypt data
+
+ CypherData - Place to store encrypted data
+
+Return Values:
+
+ STATUS_SUCCESS - Success.
+
+ STATUS_UNSUCCESSFUL - Something failed.
+--*/
+{
+ NTSTATUS Status;
+ CLEAR_BLOCK ClearBlock;
+
+ // Fill the clear block with the data value and a version number
+ ((ULONG *)&ClearBlock)[0] = Data->Length;
+ ((ULONG *)&ClearBlock)[1] = DATA_ENCRYPTION_VERSION;
+
+ Status = RtlEncryptBlock(&ClearBlock,
+ (PBLOCK_KEY)(DataKey->Pointer),
+ (PCYPHER_BLOCK)(CypherData->Pointer));
+
+ // Advance pointers
+ AdvanceCypherData(CypherData);
+ AdvanceDataKey(DataKey);
+
+ return(Status);
+}
+
+
+NTSTATUS
+EncryptFullBlock(
+ IN OUT PCRYPTP_BUFFER ClearData,
+ IN OUT PCRYPTP_BUFFER DataKey,
+ IN OUT PCRYPTP_BUFFER CypherData
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Encrypts a full block of data from ClearData and puts the encrypted
+ data in CypherData.
+ Both cleardata, datakey and cypherdata pointers are advanced.
+
+Arguments:
+
+ ClearData - Pointer to the cleardata buffer
+
+ DataKey - key to use to encrypt data
+
+ CypherData - Pointer to cypherdata buffer.
+
+Return Values:
+
+ STATUS_SUCCESS - Success.
+
+ STATUS_UNSUCCESSFUL - Something failed.
+--*/
+{
+ NTSTATUS Status;
+
+ Status = RtlEncryptBlock((PCLEAR_BLOCK)(ClearData->Pointer),
+ (PBLOCK_KEY)(DataKey->Pointer),
+ (PCYPHER_BLOCK)(CypherData->Pointer));
+
+ // Advance pointers
+ AdvanceClearData(ClearData);
+ AdvanceCypherData(CypherData);
+ AdvanceDataKey(DataKey);
+
+ return(Status);
+}
+
+
+NTSTATUS
+EncryptPartialBlock(
+ IN OUT PCRYPTP_BUFFER ClearData,
+ IN OUT PCRYPTP_BUFFER DataKey,
+ IN OUT PCRYPTP_BUFFER CypherData,
+ IN ULONG Remaining
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Encrypts a partial block of data from ClearData and puts the full
+ encrypted data block in cypherdata.
+ Both cleardata, datakey and cypherdata pointers are advanced.
+
+Arguments:
+
+ ClearData - Pointer to the cleardata buffer
+
+ DataKey - key to use to encrypt data
+
+ CypherData - Pointer to cypherdata buffer.
+
+ Remaining - the number of bytes remaining in cleardata buffer
+
+Return Values:
+
+ STATUS_SUCCESS - Success.
+
+ STATUS_UNSUCCESSFUL - Something failed.
+--*/
+{
+ NTSTATUS Status;
+ CLEAR_BLOCK ClearBlockBuffer;
+ PCLEAR_BLOCK ClearBlock = &ClearBlockBuffer;
+
+ ASSERTMSG("EncryptPartialBlock called with a block or more", Remaining < CLEAR_BLOCK_LENGTH);
+
+ // Copy the remaining bytes into a clear block buffer
+ while (Remaining > 0) {
+
+ *((PCHAR)ClearBlock) ++ = *(ClearData->Pointer) ++;
+ Remaining --;
+ }
+
+ // Zero pad
+ while (ClearBlock < &((&ClearBlockBuffer)[1])) {
+
+ *((PCHAR)ClearBlock) ++ = 0;
+ }
+
+ Status = RtlEncryptBlock(&ClearBlockBuffer,
+ (PBLOCK_KEY)(DataKey->Pointer),
+ (PCYPHER_BLOCK)(CypherData->Pointer));
+
+ // Advance pointers
+ AdvanceClearData(ClearData);
+ AdvanceCypherData(CypherData);
+ AdvanceDataKey(DataKey);
+
+ return(Status);
+}
+
+
+NTSTATUS
+DecryptDataLength(
+ IN PCRYPTP_BUFFER CypherData,
+ IN PCRYPTP_BUFFER DataKey,
+ OUT PCRYPTP_BUFFER Data
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Decrypts the data length pointed to by the cypherdata buffer and puts the
+ decrypted value in the length field of the data structure.
+ Advances the cypherdata buffer and datakey buffer pointers
+
+Arguments:
+
+ CypherData - The buffer containing the encrypted length
+
+ DataKey - key to use to decrypt data
+
+ Data - Decrypted length field is stored in the length field of this struct.
+
+Return Values:
+
+ STATUS_SUCCESS - Success.
+
+ STATUS_UNSUCCESSFUL - Something failed.
+--*/
+{
+ NTSTATUS Status;
+ CLEAR_BLOCK ClearBlock;
+ ULONG Version;
+
+ Status = RtlDecryptBlock((PCYPHER_BLOCK)(CypherData->Pointer),
+ (PBLOCK_KEY)(DataKey->Pointer),
+ &ClearBlock);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ // Advance pointers
+ AdvanceCypherData(CypherData);
+ AdvanceDataKey(DataKey);
+
+ // Copy the decrypted length into the data structure.
+ Data->Length = ((ULONG *)&ClearBlock)[0];
+
+ // Check the version
+ Version = ((ULONG *)&ClearBlock)[1];
+ if (Version != DATA_ENCRYPTION_VERSION) {
+ return(STATUS_UNKNOWN_REVISION);
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+DecryptFullBlock(
+ IN OUT PCRYPTP_BUFFER CypherData,
+ IN OUT PCRYPTP_BUFFER DataKey,
+ IN OUT PCRYPTP_BUFFER ClearData
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Decrypts a full block of data from CypherData and puts the encrypted
+ data in ClearData.
+ Both cleardata, datakey and cypherdata pointers are advanced.
+
+Arguments:
+
+ CypherData - Pointer to cypherdata buffer.
+
+ ClearData - Pointer to the cleardata buffer
+
+ DataKey - key to use to encrypt data
+
+Return Values:
+
+ STATUS_SUCCESS - Success.
+
+ STATUS_UNSUCCESSFUL - Something failed.
+--*/
+{
+ NTSTATUS Status;
+
+ Status = RtlDecryptBlock((PCYPHER_BLOCK)(CypherData->Pointer),
+ (PBLOCK_KEY)(DataKey->Pointer),
+ (PCLEAR_BLOCK)(ClearData->Pointer));
+
+ // Advance pointers
+ AdvanceClearData(ClearData);
+ AdvanceCypherData(CypherData);
+ AdvanceDataKey(DataKey);
+
+ return(Status);
+}
+
+
+NTSTATUS
+DecryptPartialBlock(
+ IN OUT PCRYPTP_BUFFER CypherData,
+ IN OUT PCRYPTP_BUFFER DataKey,
+ IN OUT PCRYPTP_BUFFER ClearData,
+ IN ULONG Remaining
+ )
+/*++
+
+Routine Description:
+
+ Internal helper routine
+
+ Decrypts a full block of data from CypherData and puts the partial
+ decrypted data block in cleardata.
+ Both cleardata, datakey and cypherdata pointers are advanced.
+
+Arguments:
+
+ CypherData - Pointer to cypherdata buffer.
+
+ ClearData - Pointer to the cleardata buffer
+
+ DataKey - key to use to encrypt data
+
+ Remaining - the number of bytes remaining in cleardata buffer
+
+Return Values:
+
+ STATUS_SUCCESS - Success.
+
+ STATUS_UNSUCCESSFUL - Something failed.
+--*/
+{
+ NTSTATUS Status;
+ CLEAR_BLOCK ClearBlockBuffer;
+ PCLEAR_BLOCK ClearBlock = &ClearBlockBuffer;
+
+ ASSERTMSG("DecryptPartialBlock called with a block or more", Remaining < CLEAR_BLOCK_LENGTH);
+
+ // Decrypt the block into a local clear block
+ Status = RtlDecryptBlock((PCYPHER_BLOCK)(CypherData->Pointer),
+ (PBLOCK_KEY)(DataKey->Pointer),
+ &ClearBlockBuffer);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ // Copy the decrypted bytes into the cleardata buffer.
+ while (Remaining > 0) {
+
+ *(ClearData->Pointer) ++ = *((PCHAR)ClearBlock) ++;
+ Remaining --;
+ }
+
+ // Advance pointers
+ AdvanceClearData(ClearData);
+ AdvanceCypherData(CypherData);
+ AdvanceDataKey(DataKey);
+
+ return(Status);
+}
+
+
+//
+// Public functions
+//
+
+
+NTSTATUS
+RtlEncryptData(
+ IN PCLEAR_DATA ClearData,
+ IN PDATA_KEY DataKey,
+ OUT PCYPHER_DATA CypherData
+ )
+
+/*++
+
+Routine Description:
+
+ Takes an arbitrary length block of data and encrypts it with a
+ data key producing an encrypted block of data.
+
+Arguments:
+
+ ClearData - The data to be encrypted.
+
+ DataKey - The key to use to encrypt the data
+
+ CypherData - Encrypted data is returned here
+
+Return Values:
+
+ STATUS_SUCCESS - The data was encrypted successfully. The encrypted
+ data is in CypherData. The length of the encrypted
+ data is is CypherData->Length.
+
+ STATUS_BUFFER_TOO_SMALL - CypherData.MaximumLength is too small to
+ contain the encrypted data.
+ CypherData->Length contains the number of bytes required.
+
+ STATUS_INVALID_PARAMETER_2 - Block key is invalid
+
+ STATUS_UNSUCCESSFUL - Something failed.
+ The CypherData is undefined.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG CypherDataLength;
+ ULONG Remaining = ClearData->Length;
+ CRYPTP_BUFFER CypherDataBuffer;
+ CRYPTP_BUFFER ClearDataBuffer;
+ CRYPTP_BUFFER DataKeyBuffer;
+ BLOCK_KEY BlockKey; // Only used if datakey less than a block long
+
+ InitializeBuffer(&ClearDataBuffer, (PCRYPT_BUFFER)ClearData);
+ InitializeBuffer(&CypherDataBuffer, (PCRYPT_BUFFER)CypherData);
+ InitializeBuffer(&DataKeyBuffer, (PCRYPT_BUFFER)DataKey);
+
+ // Check the key is OK
+ if (!ValidateDataKey(&DataKeyBuffer, &BlockKey)) {
+ return(STATUS_INVALID_PARAMETER_2);
+ }
+
+ // Find out how big we need the cypherdata buffer to be
+ CypherDataLength = CalculateCypherDataLength(&ClearDataBuffer);
+
+ // Fail if cypher data buffer too small
+ if (CypherData->MaximumLength < CypherDataLength) {
+ CypherData->Length = CypherDataLength;
+ return(STATUS_BUFFER_TOO_SMALL);
+ }
+
+ //
+ // Encrypt the clear data length into the start of the cypher data.
+ //
+ Status = EncryptDataLength(&ClearDataBuffer, &DataKeyBuffer, &CypherDataBuffer);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // Encrypt the clear data a block at a time.
+ //
+ while (Remaining >= CLEAR_BLOCK_LENGTH) {
+
+ Status = EncryptFullBlock(&ClearDataBuffer, &DataKeyBuffer, &CypherDataBuffer);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+ Remaining -= CLEAR_BLOCK_LENGTH;
+ }
+
+ //
+ // Encrypt any partial block that remains
+ //
+ if (Remaining > 0) {
+ Status = EncryptPartialBlock(&ClearDataBuffer, &DataKeyBuffer, &CypherDataBuffer, Remaining);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+ }
+
+ // Return the encrypted data length
+ CypherData->Length = CypherDataLength;
+
+ return(STATUS_SUCCESS);
+}
+
+
+
+NTSTATUS
+RtlDecryptData(
+ IN PCYPHER_DATA CypherData,
+ IN PDATA_KEY DataKey,
+ OUT PCLEAR_DATA ClearData
+ )
+/*++
+
+Routine Description:
+
+ Takes an arbitrary block of encrypted data and decrypts it with a
+ key producing the original clear block of data.
+
+Arguments:
+
+ CypherData - The data to be decrypted
+
+ DataKey - The key to use to decrypt data
+
+ ClearData - The decrpted data of data is returned here
+
+
+Return Values:
+
+ STATUS_SUCCESS - The data was decrypted successfully. The decrypted
+ data is in ClearData.
+
+ STATUS_BUFFER_TOO_SMALL - ClearData->MaximumLength is too small to
+ contain the decrypted data.
+ ClearData->Length contains the number of bytes required.
+
+ STATUS_INVALID_PARAMETER_2 - Block key is invalid
+
+ STATUS_UNSUCCESSFUL - Something failed.
+ The ClearData is undefined.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG Remaining;
+ CRYPTP_BUFFER CypherDataBuffer;
+ CRYPTP_BUFFER ClearDataBuffer;
+ CRYPTP_BUFFER DataKeyBuffer;
+ BLOCK_KEY BlockKey; // Only used if datakey less than a block long
+
+ InitializeBuffer(&ClearDataBuffer, (PCRYPT_BUFFER)ClearData);
+ InitializeBuffer(&CypherDataBuffer, (PCRYPT_BUFFER)CypherData);
+ InitializeBuffer(&DataKeyBuffer, (PCRYPT_BUFFER)DataKey);
+
+ // Check the key is OK
+ if (!ValidateDataKey(&DataKeyBuffer, &BlockKey)) {
+ return(STATUS_INVALID_PARAMETER_2);
+ }
+
+ //
+ // Decrypt the clear data length from the start of the cypher data.
+ //
+ Status = DecryptDataLength(&CypherDataBuffer, &DataKeyBuffer, &ClearDataBuffer);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ // Fail if clear data buffer too small
+ if (ClearData->MaximumLength < ClearDataBuffer.Length) {
+ ClearData->Length = ClearDataBuffer.Length;
+ return(STATUS_BUFFER_TOO_SMALL);
+ }
+
+ //
+ // Decrypt the clear data a block at a time.
+ //
+ Remaining = ClearDataBuffer.Length;
+ while (Remaining >= CLEAR_BLOCK_LENGTH) {
+
+ Status = DecryptFullBlock(&CypherDataBuffer, &DataKeyBuffer, &ClearDataBuffer);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+ Remaining -= CLEAR_BLOCK_LENGTH;
+ }
+
+ //
+ // Decrypt any partial block that remains
+ //
+ if (Remaining > 0) {
+ Status = DecryptPartialBlock(&CypherDataBuffer, &DataKeyBuffer, &ClearDataBuffer, Remaining);
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+ }
+
+ // Return the length of the decrypted data
+ ClearData->Length = ClearDataBuffer.Length;
+
+ return(STATUS_SUCCESS);
+}
+
diff --git a/private/lsa/crypt/dll/data2.c b/private/lsa/crypt/dll/data2.c
new file mode 100644
index 000000000..f3feaf27a
--- /dev/null
+++ b/private/lsa/crypt/dll/data2.c
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ data2.c
+
+Abstract:
+
+ Arbitrary length data encryption functions implementation :
+
+ RtlEncryptData2
+ RtlDecryptData2
+
+
+Author:
+
+ Richard Ward (richardw) 20 Dec 93
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+#include <engine.h>
+#include <rc4.h>
+
+
+
+NTSTATUS
+RtlEncryptData2(
+ IN OUT PCRYPT_BUFFER pData,
+ IN PDATA_KEY pKey
+ )
+
+/*++
+
+Routine Description:
+
+ Takes an arbitrary length block of data and encrypts it with a
+ data key producing an encrypted block of data.
+
+Arguments:
+
+ pData - The data that will be encrypt, IN PLACE
+
+ pKey - The key to use to encrypt the data
+
+Return Values:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ struct RC4_KEYSTRUCT Key;
+
+ if ( pData->Length != 0 ) {
+ rc4_key(&Key, pKey->Length, pKey->Buffer);
+ rc4(&Key, pData->Length, pData->Buffer);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+RtlDecryptData2(
+ IN OUT PCRYPT_BUFFER pData,
+ IN PDATA_KEY pKey
+ )
+
+/*++
+
+Routine Description:
+
+ Takes an arbitrary length block of data and encrypts it with a
+ data key producing an encrypted block of data.
+
+Arguments:
+
+ pData - The data that will be encrypt, IN PLACE
+
+ pKey - The key to use to encrypt the data
+
+Return Values:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ struct RC4_KEYSTRUCT Key;
+
+ if ( pData->Length != 0 ) {
+ rc4_key(&Key, pKey->Length, pKey->Buffer);
+ rc4(&Key, pData->Length, pData->Buffer);
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/lsa/crypt/dll/makefile b/private/lsa/crypt/dll/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/crypt/dll/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/crypt/dll/makefile.inc b/private/lsa/crypt/dll/makefile.inc
new file mode 100644
index 000000000..a7b20b921
--- /dev/null
+++ b/private/lsa/crypt/dll/makefile.inc
@@ -0,0 +1,3 @@
+obj\$(TARGET_DIRECTORY)\sys003.lib: obj\$(TARGET_DIRECTORY)\crypt.lib ..\engine\obj\$(TARGET_DIRECTORY)\engine.lib
+ lib -out:$@ $**
+
diff --git a/private/lsa/crypt/dll/owf.c b/private/lsa/crypt/dll/owf.c
new file mode 100644
index 000000000..014d0393d
--- /dev/null
+++ b/private/lsa/crypt/dll/owf.c
@@ -0,0 +1,212 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ owf.c
+
+Abstract:
+
+ Implentation of the one-way-functions used to implement password hashing.
+
+ RtlCalculateLmOwfPassword
+ RtlCalculateNtOwfPassword
+
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+#include <engine.h>
+
+
+NTSTATUS
+RtlCalculateLmOwfPassword(
+ IN PLM_PASSWORD LmPassword,
+ OUT PLM_OWF_PASSWORD LmOwfPassword
+ )
+
+/*++
+
+Routine Description:
+
+ Takes the passed LmPassword and performs a one-way-function on it.
+ The current implementation does this by using the password as a key
+ to encrypt a known block of text.
+
+Arguments:
+
+ LmPassword - The password to perform the one-way-function on.
+
+ LmOwfPassword - The hashed password is returned here
+
+Return Values:
+
+ STATUS_SUCCESS - The function was completed successfully. The hashed
+ password is in LmOwfPassword.
+
+ STATUS_UNSUCCESSFUL - Something failed. The LmOwfPassword is undefined.
+--*/
+
+{
+ NTSTATUS Status;
+ BLOCK_KEY Key[2];
+ PCHAR pKey;
+
+ // Copy the password into our key buffer and zero pad to fill the 2 keys
+
+ pKey = (PCHAR)(&Key[0]);
+
+ while (*LmPassword && (pKey < (PCHAR)(&Key[2]))) {
+ *pKey++ = *LmPassword++;
+ }
+
+ while (pKey < (PCHAR)(&Key[2])) {
+ *pKey++ = 0;
+ }
+
+
+ // Use the keys to encrypt the standard text
+
+ Status = RtlEncryptStdBlock(&Key[0], &(LmOwfPassword->data[0]));
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlEncryptStdBlock(&Key[1], &(LmOwfPassword->data[1]));
+
+ //
+ // clear our copy of the cleartext password
+ //
+
+ pKey = (PCHAR)(&Key[0]);
+
+ while (pKey < (PCHAR)(&Key[2])) {
+ *pKey++ = 0;
+ }
+
+ return(Status);
+}
+
+
+
+
+NTSTATUS
+RtlCalculateNtOwfPassword(
+ IN PNT_PASSWORD NtPassword,
+ OUT PNT_OWF_PASSWORD NtOwfPassword
+ )
+
+/*++
+
+Routine Description:
+
+ Takes the passed NtPassword and performs a one-way-function on it.
+ Uses the RSA MD4 function
+
+Arguments:
+
+ NtPassword - The password to perform the one-way-function on.
+
+ NtOwfPassword - The hashed password is returned here
+
+Return Values:
+
+ STATUS_SUCCESS - The function was completed successfully. The hashed
+ password is in NtOwfPassword.
+--*/
+
+{
+ MD4_CTX MD4_Context;
+
+
+ MD4Init(&MD4_Context);
+
+ MD4Update(&MD4_Context, (PCHAR)NtPassword->Buffer, NtPassword->Length);
+
+ MD4Final(&MD4_Context);
+
+
+ // Copy the digest into our return data area
+
+ ASSERT(sizeof(*NtOwfPassword) == sizeof(MD4_Context.digest));
+
+ RtlMoveMemory((PVOID)NtOwfPassword, (PVOID)MD4_Context.digest,
+ sizeof(*NtOwfPassword));
+
+ return(STATUS_SUCCESS);
+}
+
+
+
+BOOLEAN
+RtlEqualLmOwfPassword(
+ IN PLM_OWF_PASSWORD LmOwfPassword1,
+ IN PLM_OWF_PASSWORD LmOwfPassword2
+ )
+
+/*++
+
+Routine Description:
+
+ Compares two Lanman One-way-function-passwords
+
+Arguments:
+
+ LmOwfPassword1/2 - The one-way-functions to compare
+
+Return Values:
+
+ TRUE if the one-way-functions match, otherwise FALSE
+
+--*/
+
+{
+ return((BOOLEAN)(RtlCompareMemory(LmOwfPassword1,
+ LmOwfPassword2,
+ LM_OWF_PASSWORD_LENGTH)
+
+ == LM_OWF_PASSWORD_LENGTH));
+}
+
+
+
+BOOLEAN
+RtlEqualNtOwfPassword(
+ IN PNT_OWF_PASSWORD NtOwfPassword1,
+ IN PNT_OWF_PASSWORD NtOwfPassword2
+ )
+
+/*++
+
+Routine Description:
+
+ Compares two NT One-way-function-passwords
+
+Arguments:
+
+ NtOwfPassword1/2 - The one-way-functions to compare
+
+Return Values:
+
+ TRUE if the one-way-functions match, otherwise FALSE
+
+--*/
+
+{
+ return((BOOLEAN)(RtlCompareMemory(NtOwfPassword1,
+ NtOwfPassword2,
+ NT_OWF_PASSWORD_LENGTH)
+
+ == NT_OWF_PASSWORD_LENGTH));
+}
+
diff --git a/private/lsa/crypt/dll/owfcrypt.c b/private/lsa/crypt/dll/owfcrypt.c
new file mode 100644
index 000000000..319b1bb35
--- /dev/null
+++ b/private/lsa/crypt/dll/owfcrypt.c
@@ -0,0 +1,782 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ owdcrypt.c
+
+Abstract:
+
+ Contains functions that reversibly encrypt OwfPasswords
+
+ RtlEncryptLmOwfPwdWithLmOwfPwd
+ RtlDecryptLmOwfPwdWithLmOwfPwd
+
+ RtlEncryptLmOwfPwdWithLmSesKey
+ RtlDecryptLmOwfPwdWithLmSesKey
+
+ RtlEncryptLmOwfPwdWithUserKey
+ RtlDecryptLmOwfPwdWithUserKey
+
+ RtlEncryptLmOwfPwdWithIndex
+ RtlDecryptLmOwfPwdWithIndex
+
+ RtlEncryptNtOwfPwdWithNtOwfPwd
+ RtlDecryptNtOwfPwdWithNtOwfPwd
+
+ RtlEncryptNtOwfPwdWithNtSesKey
+ RtlDecryptNtOwfPwdWithNtSesKey
+
+ RtlEncryptNtOwfPwdWithUserKey
+ RtlDecryptNtOwfPwdWithUserKey
+
+ RtlEncryptNtOwfPwdWithIndex
+ RtlDecryptNtOwfPwdWithIndex
+
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+
+
+
+NTSTATUS
+RtlEncryptLmOwfPwdWithLmOwfPwd(
+ IN PLM_OWF_PASSWORD DataLmOwfPassword,
+ IN PLM_OWF_PASSWORD KeyLmOwfPassword,
+ OUT PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword
+ )
+
+/*++
+
+Routine Description:
+
+ Encrypts one OwfPassword with another
+
+Arguments:
+
+ DataLmOwfPassword - OwfPassword to be encrypted
+
+ KeyLmOwfPassword - OwfPassword to be used as a key to the encryption
+
+ EncryptedLmOwfPassword - The encrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedLmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The EncryptedLmOwfPassword is undefined.
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = RtlEncryptBlock((PCLEAR_BLOCK)&(DataLmOwfPassword->data[0]),
+ &(((PBLOCK_KEY)(KeyLmOwfPassword->data))[0]),
+ &(EncryptedLmOwfPassword->data[0]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlEncryptBlock((PCLEAR_BLOCK)&(DataLmOwfPassword->data[1]),
+ &(((PBLOCK_KEY)(KeyLmOwfPassword->data))[1]),
+ &(EncryptedLmOwfPassword->data[1]));
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+RtlDecryptLmOwfPwdWithLmOwfPwd(
+ IN PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword,
+ IN PLM_OWF_PASSWORD KeyLmOwfPassword,
+ OUT PLM_OWF_PASSWORD DataLmOwfPassword
+ )
+
+/*++
+
+Routine Description:
+
+ Decrypts one OwfPassword with another
+
+Arguments:
+
+ EncryptedLmOwfPassword - The ecnrypted OwfPassword to be decrypted
+
+ KeyLmOwfPassword - OwfPassword to be used as a key to the encryption
+
+ DataLmOwfPassword - The decrpted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in DataLmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The DataLmOwfPassword is undefined.
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = RtlDecryptBlock(&(EncryptedLmOwfPassword->data[0]),
+ &(((PBLOCK_KEY)(KeyLmOwfPassword->data))[0]),
+ (PCLEAR_BLOCK)&(DataLmOwfPassword->data[0]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlDecryptBlock(&(EncryptedLmOwfPassword->data[1]),
+ &(((PBLOCK_KEY)(KeyLmOwfPassword->data))[1]),
+ (PCLEAR_BLOCK)&(DataLmOwfPassword->data[1]));
+
+ return(Status);
+}
+
+
+
+
+NTSTATUS
+RtlEncryptNtOwfPwdWithNtOwfPwd(
+ IN PNT_OWF_PASSWORD DataNtOwfPassword,
+ IN PNT_OWF_PASSWORD KeyNtOwfPassword,
+ OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Encrypts one OwfPassword with another
+
+Arguments:
+
+ DataLmOwfPassword - OwfPassword to be encrypted
+
+ KeyLmOwfPassword - OwfPassword to be used as a key to the encryption
+
+ EncryptedLmOwfPassword - The encrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedLmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The EncryptedLmOwfPassword is undefined.
+--*/
+{
+ return(RtlEncryptLmOwfPwdWithLmOwfPwd(
+ (PLM_OWF_PASSWORD)DataNtOwfPassword,
+ (PLM_OWF_PASSWORD)KeyNtOwfPassword,
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword));
+}
+
+
+NTSTATUS
+RtlDecryptNtOwfPwdWithNtOwfPwd(
+ IN PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword,
+ IN PNT_OWF_PASSWORD KeyNtOwfPassword,
+ OUT PNT_OWF_PASSWORD DataNtOwfPassword
+ )
+
+/*++
+
+Routine Description:
+
+ Decrypts one OwfPassword with another
+
+Arguments:
+
+ EncryptedLmOwfPassword - The ecnrypted OwfPassword to be decrypted
+
+ KeyLmOwfPassword - OwfPassword to be used as a key to the encryption
+
+ DataLmOwfPassword - The decrpted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in DataLmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The DataLmOwfPassword is undefined.
+--*/
+
+{
+ return(RtlDecryptLmOwfPwdWithLmOwfPwd(
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword,
+ (PLM_OWF_PASSWORD)KeyNtOwfPassword,
+ (PLM_OWF_PASSWORD)DataNtOwfPassword));
+}
+
+
+
+
+NTSTATUS
+RtlEncryptLmOwfPwdWithLmSesKey(
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PLM_SESSION_KEY LmSessionKey,
+ OUT PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Encrypts an OwfPassword with a session key
+
+Arguments:
+
+ LmOwfPassword - OwfPassword to be encrypted
+
+ LmSessionKey - key to the encryption
+
+ EncryptedLmOwfPassword - The ecnrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedLmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The LMEncryptedLmOwfPassword is undefined.
+--*/
+{
+ NTSTATUS Status;
+
+ Status = RtlEncryptBlock((PCLEAR_BLOCK)&(LmOwfPassword->data[0]),
+ (PBLOCK_KEY)LmSessionKey,
+ &(EncryptedLmOwfPassword->data[0]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlEncryptBlock((PCLEAR_BLOCK)&(LmOwfPassword->data[1]),
+ (PBLOCK_KEY)LmSessionKey,
+ &(EncryptedLmOwfPassword->data[1]));
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+RtlDecryptLmOwfPwdWithLmSesKey(
+ IN PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword,
+ IN PLM_SESSION_KEY LmSessionKey,
+ OUT PLM_OWF_PASSWORD LmOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Decrypts one OwfPassword with a session key
+
+Arguments:
+
+ EncryptedLmOwfPassword - The ecnrypted OwfPassword to be decrypted
+
+ LmSessionKey - key to the encryption
+
+ LmOwfPassword - The decrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in LmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The LmOwfPassword is undefined.
+--*/
+{
+ NTSTATUS Status;
+
+
+ Status = RtlDecryptBlock(&(EncryptedLmOwfPassword->data[0]),
+ (PBLOCK_KEY)LmSessionKey,
+ (PCLEAR_BLOCK)&(LmOwfPassword->data[0]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlDecryptBlock(&(EncryptedLmOwfPassword->data[1]),
+ (PBLOCK_KEY)LmSessionKey,
+ (PCLEAR_BLOCK)&(LmOwfPassword->data[1]));
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+RtlEncryptNtOwfPwdWithNtSesKey(
+ IN PNT_OWF_PASSWORD NtOwfPassword,
+ IN PNT_SESSION_KEY NtSessionKey,
+ OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Encrypts an OwfPassword with a session key
+
+Arguments:
+
+ NtOwfPassword - OwfPassword to be encrypted
+
+ NtSessionKey - key to the encryption
+
+ EncryptedNtOwfPassword - The encrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedNtOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The EncryptedNtOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(LM_OWF_PASSWORD) == sizeof(NT_OWF_PASSWORD));
+ ASSERT(sizeof(LM_SESSION_KEY) == sizeof(NT_SESSION_KEY));
+ ASSERT(sizeof(ENCRYPTED_LM_OWF_PASSWORD) == sizeof(ENCRYPTED_NT_OWF_PASSWORD));
+
+ return(RtlEncryptLmOwfPwdWithLmSesKey(
+ (PLM_OWF_PASSWORD)NtOwfPassword,
+ (PLM_SESSION_KEY)NtSessionKey,
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword));
+}
+
+
+NTSTATUS
+RtlDecryptNtOwfPwdWithNtSesKey(
+ IN PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword,
+ IN PNT_SESSION_KEY NtSessionKey,
+ OUT PNT_OWF_PASSWORD NtOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Decrypts one OwfPassword with a session key
+
+Arguments:
+
+ EncryptedNtOwfPassword - The ecnrypted OwfPassword to be decrypted
+
+ NtSessionKey - key to the encryption
+
+ NtOwfPassword - The decrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in NtOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The NtOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(LM_OWF_PASSWORD) == sizeof(NT_OWF_PASSWORD));
+ ASSERT(sizeof(LM_SESSION_KEY) == sizeof(NT_SESSION_KEY));
+ ASSERT(sizeof(ENCRYPTED_LM_OWF_PASSWORD) == sizeof(ENCRYPTED_NT_OWF_PASSWORD));
+
+ return(RtlDecryptLmOwfPwdWithLmSesKey(
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword,
+ (PLM_SESSION_KEY)NtSessionKey,
+ (PLM_OWF_PASSWORD)NtOwfPassword));
+}
+
+
+
+VOID
+KeysFromIndex(
+ IN PCRYPT_INDEX Index,
+ OUT BLOCK_KEY Key[2])
+/*++
+
+Routine Description:
+
+ Helper function - generates 2 keys from an index value
+
+--*/
+{
+ PCHAR pKey, pIndex;
+ PCHAR IndexStart = (PCHAR)&(Index[0]);
+ PCHAR IndexEnd = (PCHAR)&(Index[1]);
+ PCHAR KeyStart = (PCHAR)&(Key[0]);
+ PCHAR KeyEnd = (PCHAR)&(Key[2]);
+
+ // Calculate the keys by concatenating the index with itself
+
+ pKey = KeyStart;
+ pIndex = IndexStart;
+
+ while (pKey < KeyEnd) {
+
+ *pKey++ = *pIndex++;
+
+ if (pIndex == IndexEnd) {
+
+ // Start at beginning of index again
+ pIndex = IndexStart;
+ }
+ }
+}
+
+
+
+NTSTATUS
+RtlEncryptLmOwfPwdWithIndex(
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PCRYPT_INDEX Index,
+ OUT PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Encrypts an OwfPassword with an index
+
+Arguments:
+
+ LmOwfPassword - OwfPassword to be encrypted
+
+ INDEX - value to be used as encryption key
+
+ EncryptedLmOwfPassword - The ecnrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedLmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The EncryptedLmOwfPassword is undefined.
+--*/
+{
+ NTSTATUS Status;
+ BLOCK_KEY Key[2];
+
+ // Calculate the keys
+
+ KeysFromIndex(Index, &(Key[0]));
+
+ // Use the keys
+
+ Status = RtlEncryptBlock((PCLEAR_BLOCK)&(LmOwfPassword->data[0]),
+ &(Key[0]),
+ &(EncryptedLmOwfPassword->data[0]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlEncryptBlock((PCLEAR_BLOCK)&(LmOwfPassword->data[1]),
+ &(Key[1]),
+ &(EncryptedLmOwfPassword->data[1]));
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+RtlDecryptLmOwfPwdWithIndex(
+ IN PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword,
+ IN PCRYPT_INDEX Index,
+ OUT PLM_OWF_PASSWORD LmOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Decrypts an OwfPassword with an index
+
+Arguments:
+
+ EncryptedLmOwfPassword - The encrypted OwfPassword to be decrypted
+
+ INDEX - value to be used as decryption key
+
+ LmOwfPassword - Decrypted OwfPassword is returned here
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in LmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The LmOwfPassword is undefined.
+--*/
+{
+ NTSTATUS Status;
+ BLOCK_KEY Key[2];
+
+ // Calculate the keys
+
+ KeysFromIndex(Index, &(Key[0]));
+
+ // Use the keys
+
+ Status = RtlDecryptBlock(&(EncryptedLmOwfPassword->data[0]),
+ &(Key[0]),
+ (PCLEAR_BLOCK)&(LmOwfPassword->data[0]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlDecryptBlock(&(EncryptedLmOwfPassword->data[1]),
+ &(Key[1]),
+ (PCLEAR_BLOCK)&(LmOwfPassword->data[1]));
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+RtlEncryptNtOwfPwdWithIndex(
+ IN PNT_OWF_PASSWORD NtOwfPassword,
+ IN PCRYPT_INDEX Index,
+ OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Encrypts an OwfPassword with an index
+
+Arguments:
+
+ NtOwfPassword - OwfPassword to be encrypted
+
+ Index - value to be used as encryption key
+
+ EncryptedNtOwfPassword - The encrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedNtOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The EncryptedNtOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(LM_OWF_PASSWORD) == sizeof(NT_OWF_PASSWORD));
+ ASSERT(sizeof(ENCRYPTED_LM_OWF_PASSWORD) == sizeof(ENCRYPTED_NT_OWF_PASSWORD));
+
+ return(RtlEncryptLmOwfPwdWithIndex(
+ (PLM_OWF_PASSWORD)NtOwfPassword,
+ Index,
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword));
+}
+
+
+
+NTSTATUS
+RtlDecryptNtOwfPwdWithIndex(
+ IN PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword,
+ IN PCRYPT_INDEX Index,
+ OUT PNT_OWF_PASSWORD NtOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Decrypts an NtOwfPassword with an index
+
+Arguments:
+
+ EncryptedNtOwfPassword - The encrypted OwfPassword to be decrypted
+
+ Index - value to be used as decryption key
+
+ NtOwfPassword - Decrypted NtOwfPassword is returned here
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in NtOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The NtOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(LM_OWF_PASSWORD) == sizeof(NT_OWF_PASSWORD));
+ ASSERT(sizeof(ENCRYPTED_LM_OWF_PASSWORD) == sizeof(ENCRYPTED_NT_OWF_PASSWORD));
+
+ return(RtlDecryptLmOwfPwdWithIndex(
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword,
+ Index,
+ (PLM_OWF_PASSWORD)NtOwfPassword));
+}
+
+
+
+
+NTSTATUS
+RtlEncryptLmOwfPwdWithUserKey(
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PUSER_SESSION_KEY UserSessionKey,
+ OUT PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Encrypts an OwfPassword with a session key
+
+Arguments:
+
+ LmOwfPassword - OwfPassword to be encrypted
+
+ UserSessionKey - key to the encryption
+
+ EncryptedLmOwfPassword - The encrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedLmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The EncryptedLmOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(USER_SESSION_KEY) == sizeof(LM_OWF_PASSWORD));
+
+ return(RtlEncryptLmOwfPwdWithLmOwfPwd(LmOwfPassword,
+ (PLM_OWF_PASSWORD)UserSessionKey,
+ EncryptedLmOwfPassword));
+}
+
+
+
+NTSTATUS
+RtlDecryptLmOwfPwdWithUserKey(
+ IN PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword,
+ IN PUSER_SESSION_KEY UserSessionKey,
+ OUT PLM_OWF_PASSWORD LmOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Decrypts one OwfPassword with a session key
+
+Arguments:
+
+ EncryptedLmOwfPassword - The ecnrypted OwfPassword to be decrypted
+
+ UserSessionKey - key to the encryption
+
+ LmOwfPassword - The decrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in LmOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The LmOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(USER_SESSION_KEY) == sizeof(LM_OWF_PASSWORD));
+
+ return(RtlDecryptLmOwfPwdWithLmOwfPwd(EncryptedLmOwfPassword,
+ (PLM_OWF_PASSWORD)UserSessionKey,
+ LmOwfPassword));
+}
+
+
+
+NTSTATUS
+RtlEncryptNtOwfPwdWithUserKey(
+ IN PNT_OWF_PASSWORD NtOwfPassword,
+ IN PUSER_SESSION_KEY UserSessionKey,
+ OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Encrypts an OwfPassword with a user session key
+
+Arguments:
+
+ NtOwfPassword - OwfPassword to be encrypted
+
+ UserSessionKey - key to the encryption
+
+ EncryptedNtOwfPassword - The encrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The encrypted
+ OwfPassword is in EncryptedNtOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The EncryptedNtOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(NT_OWF_PASSWORD) == sizeof(LM_OWF_PASSWORD));
+ ASSERT(sizeof(ENCRYPTED_NT_OWF_PASSWORD) == sizeof(ENCRYPTED_LM_OWF_PASSWORD));
+
+ return(RtlEncryptLmOwfPwdWithUserKey(
+ (PLM_OWF_PASSWORD)NtOwfPassword,
+ UserSessionKey,
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword));
+}
+
+
+
+NTSTATUS
+RtlDecryptNtOwfPwdWithUserKey(
+ IN PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword,
+ IN PUSER_SESSION_KEY UserSessionKey,
+ OUT PNT_OWF_PASSWORD NtOwfPassword
+ )
+/*++
+
+Routine Description:
+
+ Decrypts one OwfPassword with a user session key
+
+Arguments:
+
+ EncryptedNtOwfPassword - The ecnrypted OwfPassword to be decrypted
+
+ UserSessionKey - key to the encryption
+
+ NtOwfPassword - The decrypted OwfPassword is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The decrypted
+ OwfPassword is in NtOwfPassword
+
+ STATUS_UNSUCCESSFUL - Something failed. The NtOwfPassword is undefined.
+--*/
+{
+ ASSERT(sizeof(NT_OWF_PASSWORD) == sizeof(LM_OWF_PASSWORD));
+ ASSERT(sizeof(ENCRYPTED_NT_OWF_PASSWORD) == sizeof(ENCRYPTED_LM_OWF_PASSWORD));
+
+ return(RtlDecryptLmOwfPwdWithUserKey(
+ (PENCRYPTED_LM_OWF_PASSWORD)EncryptedNtOwfPassword,
+ UserSessionKey,
+ (PLM_OWF_PASSWORD)NtOwfPassword));
+}
+
diff --git a/private/lsa/crypt/dll/response.c b/private/lsa/crypt/dll/response.c
new file mode 100644
index 000000000..acfd735b7
--- /dev/null
+++ b/private/lsa/crypt/dll/response.c
@@ -0,0 +1,153 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ response.c
+
+Abstract:
+
+ Contains functions that calculate the correct response to return
+ to the server when logging on.
+
+ RtlCalculateLmResponse
+ RtlCalculateNtResponse
+
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+
+
+
+NTSTATUS
+RtlCalculateLmResponse(
+ IN PLM_CHALLENGE LmChallenge,
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ OUT PLM_RESPONSE LmResponse
+ )
+
+/*++
+
+Routine Description:
+
+ Takes the challenge sent by the server and the OwfPassword generated
+ from the password the user entered and calculates the response to
+ return to the server.
+
+Arguments:
+
+ LmChallenge - The challenge sent by the server
+
+ LmOwfPassword - The hashed password.
+
+ LmResponse - The response is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The response
+ is in LmResponse.
+
+ STATUS_UNSUCCESSFUL - Something failed. The LmResponse is undefined.
+--*/
+
+{
+ NTSTATUS Status;
+ BLOCK_KEY Key;
+ PCHAR pKey, pData;
+
+ // The first 2 keys we can get at by type-casting
+
+ Status = RtlEncryptBlock(LmChallenge,
+ &(((PBLOCK_KEY)(LmOwfPassword->data))[0]),
+ &(LmResponse->data[0]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Status = RtlEncryptBlock(LmChallenge,
+ &(((PBLOCK_KEY)(LmOwfPassword->data))[1]),
+ &(LmResponse->data[1]));
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ // To get the last key we must copy the remainder of the OwfPassword
+ // and fill the rest of the key with 0s
+
+ pKey = &(Key.data[0]);
+ pData = (PCHAR)&(((PBLOCK_KEY)(LmOwfPassword->data))[2]);
+
+ while (pData < (PCHAR)&(LmOwfPassword->data[2])) {
+ *pKey++ = *pData++;
+ }
+
+ // Zero extend
+
+ while (pKey < (PCHAR)&((&Key)[1])) {
+ *pKey++ = 0;
+ }
+
+ // Use the 3rd key
+
+ Status = RtlEncryptBlock(LmChallenge, &Key, &(LmResponse->data[2]));
+
+ return(Status);
+}
+
+
+
+
+
+
+
+NTSTATUS
+RtlCalculateNtResponse(
+ IN PNT_CHALLENGE NtChallenge,
+ IN PNT_OWF_PASSWORD NtOwfPassword,
+ OUT PNT_RESPONSE NtResponse
+ )
+/*++
+
+Routine Description:
+
+ Takes the challenge sent by the server and the OwfPassword generated
+ from the password the user entered and calculates the response to
+ return(to the server.
+
+Arguments:
+
+ NtChallenge - The challenge sent by the server
+
+ NtOwfPassword - The hashed password.
+
+ NtResponse - The response is returned here.
+
+
+Return Values:
+
+ STATUS_SUCCESS - The function completed successfully. The response
+ is in NtResponse.
+
+ STATUS_UNSUCCESSFUL - Something failed. The NtResponse is undefined.
+--*/
+
+{
+
+ // Use the LM version until we change the definitions of any of
+ // these data types
+
+ return(RtlCalculateLmResponse((PLM_CHALLENGE)NtChallenge,
+ (PLM_OWF_PASSWORD)NtOwfPassword,
+ (PLM_RESPONSE)NtResponse));
+}
diff --git a/private/lsa/crypt/dll/sources b/private/lsa/crypt/dll/sources
new file mode 100644
index 000000000..e07555cec
--- /dev/null
+++ b/private/lsa/crypt/dll/sources
@@ -0,0 +1,43 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+
+!ENDIF
+
+MAJORCOMP=CRYPT
+MINORCOMP=DLL
+
+TARGETNAME=crypt
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+INCLUDES=..\engine;..\..\..\inc
+
+SOURCES=userkey.c data.c data2.c block.c owf.c response.c owfcrypt.c
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+UMTYPE=console
+UMTEST=
+UMAPPL=
+UMLIBS=
+
+NTTARGETFILES=obj\*\sys003.lib
+
diff --git a/private/lsa/crypt/dll/userkey.c b/private/lsa/crypt/dll/userkey.c
new file mode 100644
index 000000000..c06d07651
--- /dev/null
+++ b/private/lsa/crypt/dll/userkey.c
@@ -0,0 +1,832 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ userkey.c
+
+Abstract:
+
+ Implentation of the functions that get and generate user session keys
+
+ RtlCalculateUserSessionKeyLm
+ RtlCalculateUserSessionKeyNt
+ RtlGetUserSessionKeyClient
+ RtlGetUserSessionKeyServer
+
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnfs.h>
+#include <rpc.h>
+#include <rpcndr.h>
+#include <crypt.h>
+#include <srvfsctl.h> // Server definitions
+#include <status.h> // Server return codes
+
+//
+// Define this if you want to know all about user session keys
+//
+
+// #define DEBUG_USER_SESSION_KEYS
+
+
+
+#define REDIRECTOR_DEVICENAME L"\\Device\\LanmanRedirector\\"
+#define REDIRECTOR_IPC_FILENAME L"\\IPC$"
+
+
+//
+// Define the user session key to be used for local connections
+// Make sure the initial data fills the structure completely !
+//
+
+USER_SESSION_KEY LocalSessionKey = { 'S', 'y', 's', 't', 'e', 'm', 'L', 'i',
+ 'b', 'r', 'a', 'r', 'y', 'D', 'T', 'C'
+ };
+
+//
+// Define the user session key that represents an error.
+// This value will be generated by other parts of the system on failure.
+// We will check for it in our query code and return an error if it's found.
+//
+
+USER_SESSION_KEY ErrorSessionKey = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+
+
+NTSTATUS
+RtlCalculateUserSessionKeyLm(
+ IN PLM_RESPONSE LmResponse,
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ OUT PUSER_SESSION_KEY UserSessionKey)
+
+/*++
+
+Routine Description:
+
+ Takes the passed Response and OwfPassword and generates a UserSessionKey.
+
+ The current implementation takes the one-way-function of the OwfPassword
+ and returns this as the key.
+
+Arguments:
+
+ LmResponse - The response sent during session setup.
+
+ LmOwfPassword - The hashed version of the user's password.
+
+Return Values:
+
+ STATUS_SUCCESS - The function was completed successfully.
+ The UserSessionKey is in UserSessionKey.
+
+ STATUS_UNSUCCESSFUL - Something failed. The UserSessionKey is undefined.
+--*/
+
+{
+ NTSTATUS Status;
+ NT_PASSWORD NtPassword;
+
+ //
+ // Make the Owf password look like an NT password
+ //
+
+ NtPassword.Buffer = (PWSTR)LmOwfPassword; // We can do this cast because we
+ // know the OWF routine treats this
+ // pointer as a byte pointer.
+ NtPassword.Length = sizeof(*LmOwfPassword);
+ NtPassword.MaximumLength = sizeof(*LmOwfPassword);
+
+
+ //
+ // Calculate the OWF of the OwfPassword
+ //
+
+ ASSERT(sizeof(NT_OWF_PASSWORD) == sizeof(*UserSessionKey));
+
+ Status = RtlCalculateNtOwfPassword( &NtPassword,
+ (PNT_OWF_PASSWORD)UserSessionKey
+ );
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlCalculateUserSessionKeyLm : OWF calculation failed, status = 0x%lx\n", Status));
+ return(Status);
+ }
+
+ //
+ // Check if we've generated the error session key
+ //
+
+ if (RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
+ sizeof(*UserSessionKey)) == sizeof(*UserSessionKey)) {
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlCalculateSessionKeyLm - generated error session key, modifying it\n"));
+#endif
+ //
+ // Move away from the error session key
+ //
+
+ UserSessionKey->data[0].data[0] ++;
+
+ ASSERT(RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
+ sizeof(*UserSessionKey)) != sizeof(*UserSessionKey));
+ }
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlCalculateUserSessionKeyLm : Key = 0x%lx : %lx : %lx : %lx\n",
+ ((PULONG)UserSessionKey)[0], ((PULONG)UserSessionKey)[1],
+ ((PULONG)UserSessionKey)[2], ((PULONG)UserSessionKey)[3]));
+#endif
+
+ return(STATUS_SUCCESS);
+
+ UNREFERENCED_PARAMETER(LmResponse);
+}
+
+
+
+NTSTATUS
+RtlCalculateUserSessionKeyNt(
+ IN PNT_RESPONSE NtResponse,
+ IN PNT_OWF_PASSWORD NtOwfPassword,
+ OUT PUSER_SESSION_KEY UserSessionKey)
+
+/*++
+
+Routine Description:
+
+ Takes the passed Response and OwfPassword and generates a UserSessionKey.
+
+Arguments:
+
+ NtResponse - The response sent during session setup.
+
+ NtOwfPassword - The hashed version of the user's password.
+
+Return Values:
+
+ STATUS_SUCCESS - The function was completed successfully.
+ The UserSessionKey is in UserSessionKey.
+
+ STATUS_UNSUCCESSFUL - Something failed. The UserSessionKey is undefined.
+--*/
+
+{
+ // Just call the LM version
+
+ ASSERT(sizeof(NT_RESPONSE) == sizeof(LM_RESPONSE));
+ ASSERT(sizeof(NT_OWF_PASSWORD) == sizeof(LM_OWF_PASSWORD));
+
+ return(RtlCalculateUserSessionKeyLm((PLM_RESPONSE)NtResponse,
+ (PLM_OWF_PASSWORD)NtOwfPassword,
+ UserSessionKey));
+}
+
+
+
+
+
+
+NTSTATUS
+RtlGetUserSessionKeyClient(
+ IN PVOID RpcContextHandle,
+ OUT PUSER_SESSION_KEY UserSessionKey)
+
+/*++
+
+Routine Description:
+
+ Returns the user session key associated with an rpc connection.
+ This function should be called by the client side of the connection only.
+
+Arguments:
+
+ RpcContextHandle - The rpc connection we're interested in
+
+ UserSessionKey - The user session key is returned here
+
+Return Values:
+
+ STATUS_SUCCESS - The function was completed successfully.
+ The UserSessionKey is in UserSessionKey.
+
+ STATUS_LOCAL_USER_SESSION_KEY - An informational status value.
+ - The rpc connection is local, the usersessionkey returned
+ - is constant and not unique to this connection.
+ - There is little to be gained by encrypting data over
+ - this connection
+
+ STATUS_NO_USER_SESSION_KEY - No session key exists for this session.
+
+ ------ these come from parsebinding -------
+
+ RPC_NT_OUT_OF_MEMORY - Insufficent memory is available to allocate
+ space for the fields of the string binding.
+
+ RPC_NT_INVALID_STRING_BINDING - The string binding is syntactically
+ invalid.
+
+ RPC_NT_INVALID_ARG - The string binding is not specified
+ (ie. ARGUMENT_PRESENT(StringBinding) is false).
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ RPC_BINDING_HANDLE RpcBindingHandle;
+ WCHAR *StringBinding;
+ WCHAR *ServerNameZ;
+ WCHAR *BareServerNameZ; // Points to server name minus leading '\'s
+ OBJECT_ATTRIBUTES Attributes;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING RedirDevice;
+ UNICODE_STRING IpcFileName;
+ UNICODE_STRING ServerIpcFileName;
+ USHORT LengthRequired;
+ IO_STATUS_BLOCK IoStatusBlock;
+ HANDLE RedirHandle;
+ LMR_REQUEST_PACKET RdrRequestPacket;
+ LMR_CONNECTION_INFO_2 ConnectionInfo;
+
+ //
+ // Get the binding handle for this connection
+ //
+
+ RpcBindingHandle = NDRCContextBinding((NDR_CCONTEXT)RpcContextHandle);
+
+ //
+ // Get the string description of the binding from the rpc handle
+ //
+
+ Status = (NTSTATUS)I_RpcMapWin32Status(
+ RpcBindingToStringBindingW(RpcBindingHandle, &StringBinding));
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlGetUserSessionKeyClient - failed to get stringbinding, Status = 0x%lx\n\r", Status));
+ ASSERT(FALSE);
+ return(Status);
+ }
+
+ //
+ // Parse the stringbinding to get the server name
+ //
+
+ Status = (NTSTATUS)I_RpcMapWin32Status(RpcStringBindingParseW(
+ StringBinding,
+ NULL, // object uid
+ NULL, // protseq !
+ &ServerNameZ, // network address
+ NULL, // endpoint
+ NULL // network options
+ ));
+
+ //
+ // We're finished with the string binding
+ //
+
+ IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&StringBinding));
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ //
+ // Check the result of binding parse
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlGetUserSessionKeyClient - failed to parse stringbinding, status = 0x%lx\n\r", Status));
+ ASSERT(FALSE);
+ return(Status);
+ }
+
+ //
+ // Check for a local connection
+ //
+
+ if ( (ServerNameZ == NULL) || (ServerNameZ[0] == UNICODE_NULL) ) {
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyClient - server name is NULL, returning local key\n"));
+#endif
+ //
+ // Use a constant, default session key
+ //
+
+ *UserSessionKey = LocalSessionKey;
+
+ IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ return(STATUS_LOCAL_USER_SESSION_KEY);
+ }
+
+ //
+ // Strip the leading '\'s from the server name
+ //
+
+ BareServerNameZ = ServerNameZ;
+ while (*BareServerNameZ == L'\\') {
+ BareServerNameZ ++;
+ }
+
+ //
+ // Set up a counted string for out server name
+ //
+
+ RtlInitUnicodeString(&ServerName, BareServerNameZ);
+
+
+ //
+ // Check for the local server name '.'
+ //
+
+ if ( (ServerName.Length == sizeof(*ServerName.Buffer)) &&
+ (ServerName.Buffer[0] == L'.') ) {
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyClient - server name is '.', returning local key\n"));
+#endif
+ //
+ // Use a constant, default session key
+ //
+
+ *UserSessionKey = LocalSessionKey;
+
+ IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ return(STATUS_LOCAL_USER_SESSION_KEY);
+ }
+
+
+ //
+ // Create a redirector ipc file name for the referenced server
+ //
+
+ RtlInitUnicodeString(&RedirDevice, REDIRECTOR_DEVICENAME);
+ RtlInitUnicodeString(&IpcFileName, REDIRECTOR_IPC_FILENAME);
+
+ LengthRequired = RedirDevice.Length + ServerName.Length + IpcFileName.Length;
+
+
+ //
+ // Allocate space for the ipc file name we will create
+ //
+
+ ServerIpcFileName.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, LengthRequired);
+ if (ServerIpcFileName.Buffer == NULL) {
+
+ KdPrint(("RtlGetUserSessionKeyClient - failed to allocate space for server name (%d bytes)\n", LengthRequired));
+
+ IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ ServerIpcFileName.Length = 0;
+ ServerIpcFileName.MaximumLength = LengthRequired;
+
+
+ //
+ // ServerIpcFileName = \Device\LanmanRedirector\ + servername + \ipc$
+ //
+
+ RtlCopyUnicodeString(&ServerIpcFileName, &RedirDevice);
+
+ IgnoreStatus = RtlAppendUnicodeStringToString(&ServerIpcFileName, &ServerName);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ IgnoreStatus = RtlAppendUnicodeStringToString(&ServerIpcFileName, &IpcFileName);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ //
+ // Don't need the server name any more
+ //
+
+ IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+
+
+
+ //
+ // Open up the redirector ipc file
+ //
+
+ InitializeObjectAttributes( &Attributes,
+ &ServerIpcFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ Status = NtOpenFile( &RedirHandle,
+ FILE_READ_DATA, // access required to get connection info
+ &Attributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_CREATE_TREE_CONNECTION );
+ //
+ // We're finished with the ipc filename
+ //
+
+ RtlFreeHeap( RtlProcessHeap(), 0, ServerIpcFileName.Buffer );
+ ServerIpcFileName.Buffer = NULL;
+
+ //
+ // Check the result of the open
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlGetUserSessionKeyClient - failed to open redirector, status = 0x%lx\n\r", Status));
+ ASSERT(FALSE);
+ return(Status);
+ }
+
+
+
+ //
+ // Get the connection info for this link
+ //
+
+ RdrRequestPacket.Version = REQUEST_PACKET_VERSION;
+ RdrRequestPacket.Level = 2; // We want the session key.
+
+ Status = NtFsControlFile( RedirHandle,
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock,
+ FSCTL_LMR_GET_CONNECTION_INFO,
+ &RdrRequestPacket, // Input buffer
+ sizeof(RdrRequestPacket), // Input buffer length
+ &ConnectionInfo, // Output buffer
+ sizeof(ConnectionInfo) // Output buffer length
+ );
+
+ //
+ // We're done with the redirector handle
+ //
+
+ IgnoreStatus = NtClose(RedirHandle);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ //
+ // Check the result of the control file call
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlGetUserSessionKeyClient - failed to get connection info, status = 0x%lx\n\r", Status));
+ ASSERT(FALSE);
+ return(Status);
+ }
+
+ //
+ // Copy the session key into the passed buffer
+ //
+
+ *UserSessionKey = *(PUSER_SESSION_KEY)(ConnectionInfo.UserSessionKey);
+
+
+ //
+ // Check for the error session key
+ //
+
+ if (RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
+ sizeof(*UserSessionKey)) == sizeof(*UserSessionKey)) {
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyClient - got error session key, returning error\n"));
+#endif
+ Status = STATUS_NO_USER_SESSION_KEY;
+ }
+
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyClient : Key = 0x%lx : %lx : %lx : %lx\n",
+ ((PULONG)UserSessionKey)[0], ((PULONG)UserSessionKey)[1],
+ ((PULONG)UserSessionKey)[2], ((PULONG)UserSessionKey)[3]));
+#endif
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+RtlGetUserSessionKeyServer(
+ IN PVOID RpcContextHandle OPTIONAL,
+ OUT PUSER_SESSION_KEY UserSessionKey)
+
+/*++
+
+Routine Description:
+
+ Returns the user session key associated with an rpc connection.
+ This function should be called by the server side of the connection only.
+
+Arguments:
+
+ RpcBindingHandle - The rpc connection we're interested in
+ - Note this parameter is ignored for now
+
+ UserSessionKey - The user session key is returned here
+
+Return Values:
+
+ STATUS_SUCCESS - The function was completed successfully.
+ The UserSessionKey is in UserSessionKey.
+
+ STATUS_LOCAL_USER_SESSION_KEY - An informational status value.
+ - The rpc connection is local, the usersessionkey returned
+ - is constant and not unique to this connection.
+ - There is little to be gained by encrypting data over
+ - this connection
+
+ STATUS_NO_USER_SESSION_KEY - No session key exists for this session.
+
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ HANDLE TokenHandle;
+ TOKEN_STATISTICS TokenInfo;
+ ULONG ReturnedLength;
+ UNICODE_STRING ServerDevice;
+ ANSI_STRING AnsiString;
+ OBJECT_ATTRIBUTES Attributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ HANDLE ServerHandle;
+ RPC_BINDING_HANDLE RpcBindingHandle;
+ unsigned int RpcClientLocalFlag;
+
+
+ //
+ // Get the binding handle for this connection
+ //
+
+ // LATER RpcBindingHandle = (RPC_BINDING_HANDLE) RpcContextHandle;
+ RpcBindingHandle = NULL;
+
+
+ //
+ // If this is a local connection then we can immediately
+ // return the local session key.
+ //
+
+ Status = I_RpcBindingIsClientLocal(RpcBindingHandle, &RpcClientLocalFlag);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlGetUserSessionKeyServer: RpcBindingIsClientLocal failed, status = 0x%lx\n", Status));
+ return(Status);
+ }
+
+ if (RpcClientLocalFlag != 0) {
+ *UserSessionKey = LocalSessionKey;
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyServer: client is local, returning local key\n"));
+#endif
+ return (STATUS_LOCAL_USER_SESSION_KEY);
+ }
+
+
+
+
+ //
+ // Get a handle to the client's token
+ //
+
+ Status = NtOpenThreadToken(NtCurrentThread(),
+ TOKEN_QUERY,
+ TRUE,
+ &TokenHandle);
+ //
+ // If we couldn't open the thread token because we weren't impersonating
+ // then impersonate and try again.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // Check we failed only because we weren't impersonating
+ //
+
+ if (Status != STATUS_NO_TOKEN) {
+ KdPrint(("RtlGetUserSessionKeyServer - failed to open thread token, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+ return(Status);
+ }
+
+ //
+ // Impersonate the client ourselves
+ //
+
+ Status = I_RpcMapWin32Status(RpcImpersonateClient(RpcBindingHandle));
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlGetUserSessionKeyServer - RpcImpersonateClient failed, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+ return(Status);
+ }
+
+ //
+ // Try to get a token handle now we're impersonating
+ //
+
+ Status = NtOpenThreadToken(NtCurrentThread(),
+ TOKEN_QUERY,
+ TRUE,
+ &TokenHandle);
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("RtlGetUserSessionKeyServer - failed to open thread token after impersonating, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+
+ IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf());
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ return(Status);
+ }
+
+ //
+ // We've got a token handle, stop impersonating
+ //
+
+ Status = I_RpcMapWin32Status(RpcRevertToSelf());
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("RtlGetUserSessionKeyServer - RpcRevertToSelf failed, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+
+ IgnoreStatus = NtClose(TokenHandle);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ return(Status);
+ }
+
+ }
+
+ //
+ // We've now got a token handle, get the authentication id from it.
+ //
+
+ Status = NtQueryInformationToken(
+ TokenHandle,
+ TokenStatistics,
+ &TokenInfo,
+ sizeof(TokenInfo),
+ &ReturnedLength
+ );
+
+ //
+ // We're done with the token
+ //
+
+ IgnoreStatus = NtClose(TokenHandle);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ //
+ // Check result of token query
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("RtlGetUserSessionKeyServer - Failed to query token statistics from token, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+
+ return(Status);
+ }
+
+
+
+ //
+ // Open the server device
+ //
+
+ RtlInitAnsiString(&AnsiString, SERVER_DEVICE_NAME);
+
+ Status = RtlAnsiStringToUnicodeString(&ServerDevice, &AnsiString, TRUE);
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("RtlGetUserSessionKeyServer - RtlAnsiToUnicodeString failed, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+
+ return(Status);
+ }
+
+ InitializeObjectAttributes( &Attributes,
+ &ServerDevice,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ Status = NtOpenFile( &ServerHandle,
+ GENERIC_READ | GENERIC_WRITE, // LATER use correct access
+ &Attributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ,
+ 0 );
+
+ RtlFreeUnicodeString(&ServerDevice);
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // Check for the case when the server driver is not present
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyServer - server driver not present, returning local key\n"));
+#endif
+ *UserSessionKey = LocalSessionKey;
+ Status = STATUS_LOCAL_USER_SESSION_KEY;
+
+ } else {
+ KdPrint(("RtlGetUserSessionKeyServer - Failed to open the server, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+ }
+
+ return(Status);
+ }
+
+
+ //
+ // Get the session key for this client from the server
+ //
+
+ Status = NtFsControlFile( ServerHandle,
+ NULL, // Event
+ NULL, // APC
+ NULL, // APC Context
+ &IoStatusBlock,
+ FSCTL_SRV_GET_CHALLENGE,
+ &TokenInfo.AuthenticationId,
+ sizeof(TokenInfo.AuthenticationId),
+ (PVOID)UserSessionKey,
+ sizeof(*UserSessionKey));
+ //
+ // We're done with the file handle
+ //
+
+ IgnoreStatus = NtClose(ServerHandle);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check for the error session key
+ //
+
+ if (RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
+ sizeof(*UserSessionKey)) == sizeof(*UserSessionKey)) {
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyServer - got error session key, returning error\n"));
+#endif
+ Status = STATUS_NO_USER_SESSION_KEY;
+ }
+
+ } else {
+
+ //
+ // If the server is not started or the token couldn't be found in the
+ // list of server connections, then assume it's a local connection
+ //
+
+ if ( (Status == STATUS_SERVER_NOT_STARTED) ||
+ (Status == STATUS_NO_TOKEN) ) {
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyServer - server not started or logon id not found (Status = 0x%lx), returning local key\n", Status));
+#endif
+ *UserSessionKey = LocalSessionKey;
+ Status = STATUS_LOCAL_USER_SESSION_KEY;
+
+ } else {
+ KdPrint(("RtlGetUserSessionKeyServer - Failed to query the user session key from the server, status = 0x%lx\n", Status));
+ ASSERT(FALSE);
+ }
+ }
+
+
+#ifdef DEBUG_USER_SESSION_KEYS
+ KdPrint(("RtlGetUserSessionKeyServer : Key = 0x%lx : %lx : %lx : %lx, status = 0x%lx\n",
+ ((PULONG)UserSessionKey)[0], ((PULONG)UserSessionKey)[1],
+ ((PULONG)UserSessionKey)[2], ((PULONG)UserSessionKey)[3], Status));
+#endif
+
+
+ return(Status);
+}
diff --git a/private/lsa/crypt/engine/descrypt.h b/private/lsa/crypt/engine/descrypt.h
new file mode 100644
index 000000000..6fd8e4f62
--- /dev/null
+++ b/private/lsa/crypt/engine/descrypt.h
@@ -0,0 +1,41 @@
+#define ENCRYPT 0
+#define DECRYPT 1
+
+#ifndef DECR_KEY
+#define DECR_KEY 0
+#define ENCR_KEY 1
+#define ENCR_STD 2
+#define ENCR_SES 4
+#endif
+
+#define CRYPT_ERR 1
+#define CRYPT_OK 0
+
+unsigned FAR _CRTAPI1
+DES_CBC( unsigned Option,
+ const char FAR * Key,
+ unsigned char FAR * IV,
+ unsigned char FAR * Source,
+ unsigned char FAR * Dest,
+ unsigned Size);
+
+
+unsigned FAR _CRTAPI1
+DES_CBC_LM( unsigned Option,
+ const char FAR * Key,
+ unsigned char FAR * IV,
+ unsigned char FAR * Source,
+ unsigned char FAR * Dest,
+ unsigned Size);
+
+unsigned FAR _CRTAPI1
+DES_ECB( unsigned Option,
+ const char FAR * Key,
+ unsigned char FAR * Src,
+ unsigned char FAR * Dst);
+
+unsigned FAR _CRTAPI1
+DES_ECB_LM( unsigned Option,
+ const char FAR * Key,
+ unsigned char FAR * Src,
+ unsigned char FAR * Dst);
diff --git a/private/lsa/crypt/engine/engine.h b/private/lsa/crypt/engine/engine.h
new file mode 100644
index 000000000..4042d2e6f
--- /dev/null
+++ b/private/lsa/crypt/engine/engine.h
@@ -0,0 +1,31 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ engine.h
+
+Abstract:
+
+ Defines public structures and APIs necessary to use the encryption engine
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include "..\engine\md4.h"
+
+// This header file comes to use with FAR in it.
+// Kill the FAR keyword within the file
+#ifndef FAR
+#define FAR
+#include "..\engine\descrypt.h"
+#undef FAR
+#else
+#include "..\engine\descrypt.h"
+#endif
diff --git a/private/lsa/crypt/engine/makefile b/private/lsa/crypt/engine/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/crypt/engine/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/crypt/engine/md2.h b/private/lsa/crypt/engine/md2.h
new file mode 100644
index 000000000..9afe8af26
--- /dev/null
+++ b/private/lsa/crypt/engine/md2.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+ rights reserved.
+
+ License to copy and use this software is granted for
+ non-commercial Internet Privacy-Enhanced Mail provided that it is
+ identified as the "RSA Data Security, Inc. MD2 Message Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+
+typedef struct {
+ unsigned char state[16]; /* state */
+ unsigned char checksum[16]; /* checksum */
+ unsigned int count; /* number of bytes, modulo 16 */
+ unsigned char buffer[16]; /* input buffer */
+} MD2_CTX;
+
+#define MD2Init(ctx) memset(ctx, 0, sizeof(MD2_CTX))
+
+int MD2Update(MD2_CTX *, unsigned char *, unsigned int);
+int MD2Final(MD2_CTX *);
+void MD2Transform(unsigned char [16], unsigned char [16],
+ unsigned char [16]);
+
diff --git a/private/lsa/crypt/engine/md4.h b/private/lsa/crypt/engine/md4.h
new file mode 100644
index 000000000..557d8b9f5
--- /dev/null
+++ b/private/lsa/crypt/engine/md4.h
@@ -0,0 +1,48 @@
+/*
+ **********************************************************************
+ ** md4.h -- Header file for implementation of MD4 **
+ ** RSA Data Security, Inc. MD4 Message Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ **********************************************************************
+ */
+
+/*
+ **********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD4 Message **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD4 Message Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ **********************************************************************
+ */
+
+#define UINT4 unsigned long
+
+/* Data structure for MD4 (Message Digest) computation */
+typedef struct {
+ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
+ UINT4 buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD4Final call */
+} MD4_CTX;
+
+#define MD4_LEN 16
+
+void MD4Init (MD4_CTX *);
+void MD4Update (MD4_CTX *, unsigned char *, unsigned int);
+void MD4Final (MD4_CTX *);
diff --git a/private/lsa/crypt/engine/md4c.c b/private/lsa/crypt/engine/md4c.c
new file mode 100644
index 000000000..436ae30c1
--- /dev/null
+++ b/private/lsa/crypt/engine/md4c.c
@@ -0,0 +1,223 @@
+/*
+ **********************************************************************
+ ** md4.c **
+ ** RSA Data Security, Inc. MD4 Message Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C Version **
+ **********************************************************************
+ */
+
+/*
+ **********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD4 Message **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD4 Message Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ **********************************************************************
+ */
+
+#include "md4.h"
+
+static void Transform(UINT4 *, UINT4 *);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G and H are basic MD4 functions: selection, majority, parity */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are MD4 transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s) \
+ {(a) += F ((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT ((a), (s));}
+#define GG(a, b, c, d, x, s) \
+ {(a) += G ((b), (c), (d)) + (x) + (UINT4)013240474631; \
+ (a) = ROTATE_LEFT ((a), (s));}
+#define HH(a, b, c, d, x, s) \
+ {(a) += H ((b), (c), (d)) + (x) + (UINT4)015666365641; \
+ (a) = ROTATE_LEFT ((a), (s));}
+
+void MD4Init (MD4_CTX * mdContext)
+{
+ mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+ /* Load magic initialization constants.
+ */
+ mdContext->buf[0] = (UINT4)0x67452301;
+ mdContext->buf[1] = (UINT4)0xefcdab89;
+ mdContext->buf[2] = (UINT4)0x98badcfe;
+ mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+void MD4Update( MD4_CTX * mdContext,
+ unsigned char * inBuf,
+ unsigned int inLen)
+
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+ mdContext->i[1]++;
+ mdContext->i[0] += ((UINT4)inLen << 3);
+ mdContext->i[1] += ((UINT4)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer */
+ mdContext->in[mdi] = *inBuf++;
+
+ /* increment mdi */
+ mdi++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+void MD4Final (MD4_CTX * mdContext)
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD4Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+}
+
+/* Basic MD4 step. Transform buf based on in.
+ */
+static void Transform (UINT4 * buf,
+ UINT4 * in)
+{
+ UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+ FF (a, b, c, d, in[ 0], 3);
+ FF (d, a, b, c, in[ 1], 7);
+ FF (c, d, a, b, in[ 2], 11);
+ FF (b, c, d, a, in[ 3], 19);
+ FF (a, b, c, d, in[ 4], 3);
+ FF (d, a, b, c, in[ 5], 7);
+ FF (c, d, a, b, in[ 6], 11);
+ FF (b, c, d, a, in[ 7], 19);
+ FF (a, b, c, d, in[ 8], 3);
+ FF (d, a, b, c, in[ 9], 7);
+ FF (c, d, a, b, in[10], 11);
+ FF (b, c, d, a, in[11], 19);
+ FF (a, b, c, d, in[12], 3);
+ FF (d, a, b, c, in[13], 7);
+ FF (c, d, a, b, in[14], 11);
+ FF (b, c, d, a, in[15], 19);
+
+ /* Round 2 */
+ GG (a, b, c, d, in[ 0], 3);
+ GG (d, a, b, c, in[ 4], 5);
+ GG (c, d, a, b, in[ 8], 9);
+ GG (b, c, d, a, in[12], 13);
+ GG (a, b, c, d, in[ 1], 3);
+ GG (d, a, b, c, in[ 5], 5);
+ GG (c, d, a, b, in[ 9], 9);
+ GG (b, c, d, a, in[13], 13);
+ GG (a, b, c, d, in[ 2], 3);
+ GG (d, a, b, c, in[ 6], 5);
+ GG (c, d, a, b, in[10], 9);
+ GG (b, c, d, a, in[14], 13);
+ GG (a, b, c, d, in[ 3], 3);
+ GG (d, a, b, c, in[ 7], 5);
+ GG (c, d, a, b, in[11], 9);
+ GG (b, c, d, a, in[15], 13);
+
+ /* Round 3 */
+ HH (a, b, c, d, in[ 0], 3);
+ HH (d, a, b, c, in[ 8], 9);
+ HH (c, d, a, b, in[ 4], 11);
+ HH (b, c, d, a, in[12], 15);
+ HH (a, b, c, d, in[ 2], 3);
+ HH (d, a, b, c, in[10], 9);
+ HH (c, d, a, b, in[ 6], 11);
+ HH (b, c, d, a, in[14], 15);
+ HH (a, b, c, d, in[ 1], 3);
+ HH (d, a, b, c, in[ 9], 9);
+ HH (c, d, a, b, in[ 5], 11);
+ HH (b, c, d, a, in[13], 15);
+ HH (a, b, c, d, in[ 3], 3);
+ HH (d, a, b, c, in[11], 9);
+ HH (c, d, a, b, in[ 7], 11);
+ HH (b, c, d, a, in[15], 15);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
diff --git a/private/lsa/crypt/engine/md5.h b/private/lsa/crypt/engine/md5.h
new file mode 100644
index 000000000..8a5bdf59b
--- /dev/null
+++ b/private/lsa/crypt/engine/md5.h
@@ -0,0 +1,69 @@
+/*
+ ***********************************************************************
+ ** md5.h -- Header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ ULONG i[2]; /* number of _bits_ handled mod 2^64 */
+ ULONG buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+ ULONG FinishFlag;
+} MD5_CTX;
+
+
+#define MD5DIGESTLEN 16
+
+#define PROTO_LIST(list) list
+
+
+/*
+ * MTS: Each of these assumes MD5_CTX is locked against simultaneous use.
+ */
+void MD5Init PROTO_LIST ((MD5_CTX *));
+void MD5Update PROTO_LIST ((MD5_CTX *, const unsigned char *, unsigned int));
+void MD5Final PROTO_LIST ((MD5_CTX *));
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/private/lsa/crypt/engine/obj/i386/rsa32.foo b/private/lsa/crypt/engine/obj/i386/rsa32.foo
new file mode 100644
index 000000000..6caf35955
--- /dev/null
+++ b/private/lsa/crypt/engine/obj/i386/rsa32.foo
Binary files differ
diff --git a/private/lsa/crypt/engine/rc4.h b/private/lsa/crypt/engine/rc4.h
new file mode 100644
index 000000000..a26c2d173
--- /dev/null
+++ b/private/lsa/crypt/engine/rc4.h
@@ -0,0 +1,10 @@
+/* Key structure */
+struct RC4_KEYSTRUCT
+{
+ unsigned char S[256]; /* State table */
+ unsigned char i,j; /* Indices */
+};
+
+void rc4_key(struct RC4_KEYSTRUCT *, int, PUCHAR);
+void rc4(struct RC4_KEYSTRUCT *, int , PUCHAR);
+
diff --git a/private/lsa/crypt/engine/rsa.h b/private/lsa/crypt/engine/rsa.h
new file mode 100644
index 000000000..9b69ca4b2
--- /dev/null
+++ b/private/lsa/crypt/engine/rsa.h
@@ -0,0 +1,272 @@
+/* rsa.h
+ *
+ * RSA library functions.
+ *
+ * Copyright (C) RSA Data Security, Inc. created 1990. This is an
+ * unpublished work protected as such under copyright law. This work
+ * contains proprietary, confidential, and trade secret information of
+ * RSA Data Security, Inc. Use, disclosure or reproduction without the
+ * express written authorization of RSA Data Security, Inc. is
+ * prohibited.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RSA1 ((DWORD)'R'+((DWORD)'S'<<8)+((DWORD)'A'<<16)+((DWORD)'1'<<24))
+#define RSA2 ((DWORD)'R'+((DWORD)'S'<<8)+((DWORD)'A'<<16)+((DWORD)'2'<<24))
+
+// Key header structures.
+//
+// These structs define the fixed data at the beginning of an RSA key.
+// They are followed by a variable length of data, sized by the stlen
+// field.
+
+typedef struct {
+ DWORD magic; /* Should always be RSA1 */
+ DWORD keylen; // size of modulus buffer
+ DWORD bitlen; // # of bits in modulus
+ DWORD datalen; // max number of bytes to be encoded
+ DWORD pubexp; //public exponent
+} BSAFE_PUB_KEY, FAR *LPBSAFE_PUB_KEY;
+
+typedef struct {
+ DWORD magic; /* Should always be RSA2 */
+ DWORD keylen; // size of modulus buffer
+ DWORD bitlen; // bit size of key
+ DWORD datalen; // max number of bytes to be encoded
+ DWORD pubexp; // public exponent
+} BSAFE_PRV_KEY, FAR *LPBSAFE_PRV_KEY;
+
+typedef struct {
+ BYTE *modulus;
+ BYTE *prvexp;
+ BYTE *prime1;
+ BYTE *prime2;
+ BYTE *exp1;
+ BYTE *exp2;
+ BYTE *coef;
+ BYTE *invmod;
+ BYTE *invpr1;
+ BYTE *invpr2;
+} BSAFE_KEY_PARTS, FAR *LPBSAFE_KEY_PARTS;
+
+typedef const BYTE far *cLPBYTE; // const LPBYTE resolves wrong
+
+/* BSafeEncPublic
+ *
+ * BSafeEncPublic(key, part_in, part_out)
+ *
+ * RSA encrypt a buffer of size key->keylen, filled with data of size
+ * key->datalen with the public key pointed to by key, returning the
+ * encrypted data in part_out.
+ *
+ * Parameters
+ *
+ * LPBSAFE_PUB_KEY key - points to a public key in BSAFE_KEY
+ * format.
+ *
+ * LPBYTE part_in - points to a BYTE array of size key->keylen
+ * holding the data to be encrypted. The
+ * data in the buffer should be no larger
+ * than key->datalen. All other bytes should
+ * be zero.
+ *
+ * LPBYTE part_out - points to a BYTE array of size keylen
+ * to receive the encrypted data.
+ *
+ * Returns
+ *
+ * TRUE - encryption succeeded.
+ * FALSE - encryption failed.
+ *
+ */
+
+BOOL BSafeEncPublic(const LPBSAFE_PUB_KEY key,
+ cLPBYTE part_in,
+ LPBYTE part_out);
+
+
+/* BSafeDecPrivate
+ *
+ * BSafeDecPrivate(key, part_in, part_out)
+ *
+ * RSA decrypt a buffer of size keylen, containing key->datalen bytes
+ * of data with the private key pointed to by key, returning the
+ * decrypted data in part_out.
+ *
+ * Parameters
+ *
+ * LPBSAFE_PRV_KEY key - points to a private key in BSAFE_KEY
+ * format.
+ *
+ * LPBYTE part_in - points to a BYTE array of size key->keylen
+ * holding the data to be decrypted. The data
+ * in the buffer should be no longer than
+ * key->datalen. All other bytes should be zero.
+ *
+ * LPBYTE part_out - points to a BYTE array of size GRAINSIZE
+ * to receive the decrypted data.
+ *
+ * Returns
+ *
+ * TRUE - decryption succeeded.
+ * FALSE - decryption failed.
+ *
+ */
+
+BOOL BSafeDecPrivate(const LPBSAFE_PRV_KEY key,
+ cLPBYTE part_in,
+ LPBYTE part_out);
+
+/* BSafeMakeKeyPair
+ *
+ * BSafeMakeKeyPair(public_key, private_key, bits)
+ *
+ * Generate an RSA key pair.
+ *
+ * Parameters
+ *
+ * LPBSAFE_PUB_KEY public_key - points to the memory to recieve
+ * the public key. This pointer must
+ * point to at least the number of bytes
+ * specified as the public key size by
+ * BSafeComputeKeySizes.
+ *
+ * LPBSAFE_PRV_KEY private_key - points to the memory to recieve
+ * the private key. This pointer must
+ * point to at least the number of bytes
+ * specified as the private key size
+ * by BSafeComputeKeySizes.
+ *
+ * DWORD bits - length of the requested key in bits.
+ * This value must be even and greater than 63
+ *
+ * Returns
+ *
+ * TRUE - keys were successfully generated
+ * FALSE - not enough memory to generate keys
+ *
+ */
+
+BOOL BSafeMakeKeyPair(LPBSAFE_PUB_KEY public_key,
+ LPBSAFE_PRV_KEY private_key,
+ DWORD bits);
+
+/* BSafeFreePubKey
+ *
+ * BSafeFreePubKey(public_key)
+ *
+ * Free the data associated with a public key
+ *
+ * Parameters
+ *
+ * LPBSAFE_PUB_KEY public_key - points to a BSAFE_PUB_KEY
+ * structure to free.
+ *
+ * Returns
+ *
+ * nothing
+ *
+ */
+
+void BSafeFreePubKey(LPBSAFE_PUB_KEY public_key);
+
+/* BSafeFreePrvKey
+ *
+ * BSafeFreePrvKey(public_key)
+ *
+ * Free the data associated with a private key
+ *
+ * Parameters
+ *
+ * LPBSAFE_PRV_KEY private_key - points to a BSAFE_PRV_KEY
+ * structure to free.
+ *
+ * Returns
+ *
+ * nothing
+ *
+ */
+
+void BSafeFreePrvKey(LPBSAFE_PRV_KEY private_key);
+
+
+/* BSafeComputeKeySizes
+ *
+ * BSafeComputeKeySizes( LPDWORD PubKeySize,
+ * LPDWORD PrivKeySize,
+ * LPDWORD bits )
+ *
+ * Computes the required memory to hold a public and private key of
+ * a specified number of bits.
+ *
+ * Parameters:
+ *
+ * LPDWORD PubKeySize - pointer to DWORD to return the public
+ * key size, in bytes.
+ *
+ * LPDWORD PrivKeySize - pointer to DWORD to return the private
+ * key size, in bytes.
+ *
+ * LPDWORD bits - pointer to DWORD specifying number of bits
+ * in the RSA modulus.
+ *
+ * Returns:
+ *
+ * TRUE if *bits is a valid RSA modulus size.
+ * FALSE if *bits is an invalid RSA modulus size.
+ *
+ */
+
+BOOL BSafeComputeKeySizes(LPDWORD PublicKeySize,
+ LPDWORD PrivateKeySize,
+ LPDWORD bits);
+
+/* BSafeGetPrvKeyParts
+ *
+ * BOOL BSafeGetPrvKeyParts( LPBSAFE_PRV_KEY key,
+ * LPBSAFE_KEY_PARTS parts)
+ *
+ * Returns pointers to the parts of a private key, and the length of
+ * the modulus in bytes.
+ *
+ * Parameters:
+ *
+ * LPBSAFE_PRV_KEY key - the key to disassemble
+ * LPBSAFE_KEY_PARTS parts - the structure to fill in
+ *
+ * Returns -
+ * FALSE if the key is not valid.
+ */
+
+BOOL BSafeGetPrvKeyParts(LPBSAFE_PRV_KEY key,
+ LPBSAFE_KEY_PARTS parts);
+
+
+/* BSafeGetPubKeyModulus
+ *
+ * BYTE *BSafeGetPubKeyModulus(LPBSAFE_PUB_KEY key)
+ *
+ * Returns pointer to the modulus of a public key
+ *
+ * Parameters:
+ *
+ * LPBSAFE_PUB_KEY key - the key to disassemble
+ *
+ * Returns -
+ *
+ * Pointer to the parts, VOID on error.
+ * Fails if the key is not valid.
+ */
+
+BYTE *BSafeGetPubKeyModulus(LPBSAFE_PUB_KEY key);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
diff --git a/private/lsa/crypt/engine/sha.h b/private/lsa/crypt/engine/sha.h
new file mode 100644
index 000000000..a384db920
--- /dev/null
+++ b/private/lsa/crypt/engine/sha.h
@@ -0,0 +1,32 @@
+/* Copyright (C) RSA Data Security, Inc. created 1993. This is an
+ unpublished work protected as such under copyright law. This work
+ contains proprietary, confidential, and trade secret information of
+ RSA Data Security, Inc. Use, disclosure or reproduction without the
+ express written authorization of RSA Data Security, Inc. is
+ prohibited.
+ */
+
+#ifndef _SHA_H_
+#define _SHA_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "shacomm.h"
+
+typedef struct {
+ DWORD FinishFlag;
+ BYTE HashVal[A_SHA_DIGEST_LEN];
+ A_SHA_COMM_CTX commonContext;
+} A_SHA_CTX;
+
+void A_SHAInit(A_SHA_CTX *);
+void A_SHAUpdate(A_SHA_CTX *, unsigned char *, unsigned int);
+void A_SHAFinal(A_SHA_CTX *, unsigned char [A_SHA_DIGEST_LEN]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/lsa/crypt/engine/shacomm.h b/private/lsa/crypt/engine/shacomm.h
new file mode 100644
index 000000000..224176ede
--- /dev/null
+++ b/private/lsa/crypt/engine/shacomm.h
@@ -0,0 +1,30 @@
+/* Copyright (C) RSA Data Security, Inc. created 1993. This is an
+ unpublished work protected as such under copyright law. This work
+ contains proprietary, confidential, and trade secret information of
+ RSA Data Security, Inc. Use, disclosure or reproduction without the
+ express written authorization of RSA Data Security, Inc. is
+ prohibited.
+ */
+
+#ifndef _SHACOMM_H_
+#define _SHACOMM_H_ 1
+
+#define A_SHA_DIGEST_LEN 20
+
+typedef struct {
+ DWORD state[5]; /* state (ABCDE) */
+ DWORD count[2]; /* number of bytes, msb first */
+ unsigned char buffer[64]; /* input buffer */
+} A_SHA_COMM_CTX;
+
+typedef void (A_SHA_TRANSFORM) (DWORD [5], unsigned char [64]);
+
+void A_SHAInitCommon (A_SHA_COMM_CTX *);
+void A_SHAUpdateCommon(A_SHA_COMM_CTX *, BYTE *, DWORD, A_SHA_TRANSFORM *);
+void A_SHAFinalCommon(A_SHA_COMM_CTX *, BYTE[A_SHA_DIGEST_LEN],
+ A_SHA_TRANSFORM *);
+
+void DWORDToBigEndian(unsigned char *, DWORD *, unsigned int);
+void DWORDFromBigEndian(DWORD *, unsigned int, unsigned char *);
+
+#endif
diff --git a/private/lsa/crypt/engine/sources b/private/lsa/crypt/engine/sources
new file mode 100644
index 000000000..79317c6ef
--- /dev/null
+++ b/private/lsa/crypt/engine/sources
@@ -0,0 +1,39 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+
+!ENDIF
+
+MAJORCOMP=CRYPT
+MINORCOMP=ENGINE
+
+TARGETNAME=ENGINE
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+TARGETLIBS=
+
+SOURCES=md4c.c des.c cbc.c ecb.c rc4c.c
+
+UMTYPE=console
+UMAPPL=
+UMLIBS=
+
diff --git a/private/lsa/crypt/test/ctauto.c b/private/lsa/crypt/test/ctauto.c
new file mode 100644
index 000000000..3cf56ef7b
--- /dev/null
+++ b/private/lsa/crypt/test/ctauto.c
@@ -0,0 +1,820 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ct1.c
+
+Abstract:
+
+ Automatic component test program for NT ecnryption library
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+#include <string.h>
+#include <stdlib.h>
+#include "ctutil.h"
+
+#define END_TO_END_ITERATIONS 100
+#define MAX_DATA_LENGTH 42 // Maximum length data block for end-end test
+
+
+BOOLEAN Success;
+
+
+VOID
+GenerateRandomBlock(
+ PCLEAR_BLOCK ClearBlock,
+ PULONG Seed)
+{
+ *(&(((PULONG)ClearBlock)[0])) = RtlRandom(Seed);
+ *(&(((PULONG)ClearBlock)[1])) = RtlRandom(Seed);
+}
+
+VOID
+GenerateRandomData(
+ PCLEAR_DATA ClearData,
+ PULONG Seed)
+{
+ ULONG Index;
+
+ for (Index =0;
+ Index < (ClearData->MaximumLength - CLEAR_BLOCK_LENGTH);
+ Index += CLEAR_BLOCK_LENGTH) {
+
+ GenerateRandomBlock((PCLEAR_BLOCK)&(((PCHAR)(ClearData->Buffer))[Index]), Seed);
+ }
+}
+
+
+VOID
+GenerateSeed(
+ PULONG Seed)
+{
+ LARGE_INTEGER Time;
+
+ NtQuerySystemTime(&Time);
+ *Seed = ((PLONG)(&Time))[0] ^ ((PLONG)(&Time))[1];
+}
+
+
+VOID
+GenerateRandomKey(
+ PBLOCK_KEY BlockKey,
+ PULONG Seed)
+{
+ ULONG Data;
+
+ *(&(((PULONG)BlockKey)[0])) = RtlRandom(Seed);
+ Data = RtlRandom(Seed);
+
+ BlockKey->data[4] = (CHAR)(Data & 0xff);
+ BlockKey->data[5] = (CHAR)((Data >> 8 ) & 0xff);
+ BlockKey->data[6] = (CHAR)((Data >> 16) & 0xff);
+}
+
+
+VOID
+GenerateRandomLmOwfPassword(
+ PLM_OWF_PASSWORD LmOwfPassword,
+ PULONG Seed)
+{
+ GenerateRandomBlock((PCLEAR_BLOCK)&(LmOwfPassword->data[0]), Seed);
+ GenerateRandomBlock((PCLEAR_BLOCK)&(LmOwfPassword->data[1]), Seed);
+}
+
+
+VOID
+GenerateRandomNtOwfPassword(
+ PNT_OWF_PASSWORD NtOwfPassword,
+ PULONG Seed)
+{
+ ASSERT(sizeof(LM_OWF_PASSWORD) == sizeof(NT_OWF_PASSWORD));
+
+ GenerateRandomLmOwfPassword((PLM_OWF_PASSWORD)NtOwfPassword, Seed);
+}
+
+
+VOID
+GenerateRandomLmSessionKey(
+ PLM_SESSION_KEY LmSessionKey,
+ PULONG Seed)
+{
+ GenerateRandomBlock((PCLEAR_BLOCK)LmSessionKey, Seed);
+}
+
+
+VOID
+GenerateRandomNtSessionKey(
+ PNT_SESSION_KEY NtSessionKey,
+ PULONG Seed)
+{
+ ASSERT(sizeof(NT_SESSION_KEY) == sizeof(LM_SESSION_KEY));
+
+ GenerateRandomLmSessionKey((PLM_SESSION_KEY)NtSessionKey, Seed);
+}
+
+
+VOID
+GenerateRandomUserSessionKey(
+ PUSER_SESSION_KEY UserSessionKey,
+ PULONG Seed)
+{
+ ASSERT(sizeof(USER_SESSION_KEY) == sizeof(LM_OWF_PASSWORD));
+
+ GenerateRandomLmOwfPassword((PLM_OWF_PASSWORD)UserSessionKey, Seed);
+}
+
+
+VOID
+GenerateRandomCryptIndex(
+ PCRYPT_INDEX CryptIndex,
+ PULONG Seed)
+{
+ *CryptIndex = (CRYPT_INDEX)RtlRandom(Seed);
+}
+
+
+
+VOID
+TestEndToEnd(VOID)
+{
+ // Test the block encryption routines end-to-end
+
+ int i;
+ ULONG Seed;
+ CLEAR_BLOCK ClearBlock1, ClearBlock2;
+ CYPHER_BLOCK CypherBlock;
+ BLOCK_KEY BlockKey;
+
+ CtPrint("Testing end-to-end block encryption/decryption...");
+
+ GenerateSeed(&Seed);
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ // Generate a random clearblock and blockkey
+ GenerateRandomBlock(&ClearBlock1, &Seed);
+ GenerateRandomKey(&BlockKey, &Seed);
+
+ // Encrypt and then decrypt block
+ RtlEncryptBlock(&ClearBlock1, &BlockKey, &CypherBlock);
+ RtlDecryptBlock(&CypherBlock, &BlockKey, &ClearBlock2);
+
+ if (!CtEqualClearBlock(&ClearBlock1, &ClearBlock2)) {
+ CtPrint("\n\rError - block did not decrypt to same value as original\n\r");
+ CtPrint("Clear Block :\n\r");
+ CtPrintClearBlocks(&ClearBlock1, 1);
+ CtPrint("Block Key :\n\r");
+ CtPrintBlockKeys(&BlockKey, 1);
+ CtPrint("Cypher Block :\n\r");
+ CtPrintCypherBlocks(&CypherBlock, 1);
+ CtPrint("Resulting Clear Block :\n\r");
+ CtPrintClearBlocks(&ClearBlock2, 1);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+}
+
+
+VOID
+TestDataEndToEnd(VOID)
+{
+ // Test the data encryption routines end-to-end
+
+ NTSTATUS Status;
+
+ ULONG Seed;
+ CHAR ClearBuffer1[MAX_DATA_LENGTH], ClearBuffer2[MAX_DATA_LENGTH];
+ CLEAR_DATA ClearData1, ClearData2;
+ CHAR CypherBuffer[MAX_DATA_LENGTH + (CYPHER_BLOCK_LENGTH * 2)];
+ CYPHER_DATA CypherData;
+ CHAR KeyBuffer[MAX_DATA_LENGTH + (CYPHER_BLOCK_LENGTH * 2)];
+ DATA_KEY DataKey;
+ ULONG DataLength;
+ ULONG KeyLength;
+ CHAR BufferCheck;
+
+ CtPrint("\n\rTesting end-to-end data encryption/decryption...");
+
+ GenerateSeed(&Seed);
+
+ ClearData1.Buffer = ClearBuffer1 + 1;
+ ClearData1.MaximumLength = sizeof(ClearBuffer1) - 1;
+ ClearData2.Buffer = ClearBuffer2 + 1;
+ ClearData2.MaximumLength = sizeof(ClearBuffer2) - 1;
+ CypherData.Buffer = CypherBuffer + 1;
+ CypherData.MaximumLength = sizeof(CypherBuffer) - 1;
+ DataKey.Buffer = KeyBuffer + 1;
+ DataKey.MaximumLength = sizeof(KeyBuffer) - 1;
+
+ // Generate a random clearblock and blockkey
+ GenerateRandomData(&ClearData1, &Seed);
+ GenerateRandomData((PCLEAR_DATA)&DataKey, &Seed);
+
+ for (DataLength = 1; DataLength <= ClearData1.MaximumLength; DataLength ++) {
+
+ ClearData1.Length = DataLength;
+
+ for (KeyLength = 1; KeyLength <= BLOCK_KEY_LENGTH*2; KeyLength++) {
+
+ DataKey.Length = KeyLength;
+
+ // Encrypt and then decrypt data
+ CypherData.MaximumLength = 0;
+ Status = RtlEncryptData(&ClearData1, &DataKey, &CypherData);
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+ ASSERT(CypherData.Length <= sizeof(CypherBuffer));
+ CypherData.MaximumLength = sizeof(CypherBuffer);
+
+ BufferCheck = ((PCHAR)CypherData.Buffer)[CypherData.Length];
+ Status = RtlEncryptData(&ClearData1, &DataKey, &CypherData);
+ if (!NT_SUCCESS(Status)) {
+ CtPrint("RtlEncryptData failed - status = 0x%lx\n\r", Status);
+ Success = FALSE;
+ break;
+ }
+ if (BufferCheck != ((PCHAR)CypherData.Buffer)[CypherData.Length]) {
+ CtPrint("RtlEncryptData wrote past end of buffer\n\r");
+ Success = FALSE;
+ break;
+ }
+
+ // Modify byte beyond end and start of encrypted data to check it's not used
+ ((PCHAR)CypherData.Buffer)[CypherData.Length] --;
+ (*CypherBuffer) ++;
+ // Modify byte beyond end and start of key to check it's not used
+ ((PCHAR)DataKey.Buffer)[DataKey.Length] --;
+ (*KeyBuffer) ++;
+
+ ClearData2.MaximumLength = 0;
+ Status = RtlDecryptData(&CypherData, &DataKey, &ClearData2);
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+ ASSERT(ClearData2.Length <= sizeof(ClearBuffer2));
+ ClearData2.MaximumLength = sizeof(ClearBuffer2);
+
+ RtlFillMemory(ClearData2.Buffer, ClearData2.Length, 'f');
+
+ BufferCheck = ((PCHAR)ClearData2.Buffer)[ClearData2.Length];
+ Status = RtlDecryptData(&CypherData, &DataKey, &ClearData2);
+ if (!NT_SUCCESS(Status)) {
+ CtPrint("RtlDecryptData failed - status = 0x%lx\n\r", Status);
+ Success = FALSE;
+ break;
+ }
+ if (BufferCheck != ((PCHAR)ClearData2.Buffer)[ClearData2.Length]) {
+ CtPrint("RtlDecryptData wrote past end of buffer\n\r");
+ Success = FALSE;
+ break;
+ }
+
+ //DbgPrint("Clear length1 = %ld, cypher length = %ld, clear length2 = %ld\n\r",
+ // ClearData1.Length, CypherData.Length, ClearData2.Length);
+
+ if (!CtEqualClearData(&ClearData1, &ClearData2)) {
+ CtPrint("\n\rError - data did not decrypt to same value as original\n\r");
+ CtPrint("Clear Data :\n\r");
+ CtPrintClearData(&ClearData1);
+ CtPrint("Data Key :\n\r");
+ CtPrintDataKey(&DataKey);
+ CtPrint("Cypher Data :\n\r");
+ CtPrintCypherData(&CypherData);
+ CtPrint("Resulting Clear Data :\n\r");
+ CtPrintClearData(&ClearData2);
+ Success = FALSE;
+ break;
+ }
+ }
+ }
+
+ CtPrint("Done.\n\r");
+}
+
+ typedef struct {
+ LM_CHALLENGE LmChallenge;
+ PLM_PASSWORD LmPassword;
+ LM_RESPONSE LmResponse;
+ } LMTESTCASE;
+
+
+VOID
+TestLMCompat(VOID)
+{
+ LM_RESPONSE LmResponse;
+ int i;
+
+
+ LMTESTCASE testcases[] = {
+
+ { // Test-case 0
+
+ { 0x15, 0x24, 0xd3, 0x16, 0x58, 0xc0, 0xe6, 0xc6 }, // challenge
+ { "NG4200" }, // password
+ { 0x8e, 0x45, 0x82, 0x3d, 0xd2, 0xdb, 0x5b, 0x3a, // response
+ 0x05, 0x8d, 0x88, 0x9e, 0x6e, 0x19, 0xe8, 0xc8, // response
+ 0xb8, 0xd6, 0xa8, 0x13, 0xfd, 0x54, 0x84, 0x0c }, // response
+ },
+ { // Test-case 1
+
+ { 0x84, 0x58, 0xcf, 0x54, 0x79, 0x09, 0x0a, 0xbe }, // challenge
+ { "A" }, // password
+ { 0x3b, 0x43, 0xf7, 0x83, 0x65, 0xdd, 0x5d, 0xf9, // response
+ 0xd1, 0x50, 0x4b, 0x55, 0x37, 0x8d, 0x9e, 0x58, // response
+ 0x57, 0xea, 0xe5, 0x7b, 0xfc, 0x02, 0x29, 0x08 }, // response
+ },
+ { // Test-case 2
+
+ { 0x9d, 0x5b, 0x20, 0x48, 0xa4, 0x37, 0x4e, 0xf4 }, // challenge
+ { "01234567890123" }, // password
+ { 0xe9, 0x93, 0xdf, 0x31, 0xd0, 0xa1, 0xba, 0x97, // response
+ 0x53, 0x32, 0x36, 0x1e, 0x8f, 0x76, 0x13, 0xe2, // response
+ 0x60, 0xf9, 0xfe, 0xfa, 0x70, 0xf0, 0xb1, 0x0d }, // response
+ },
+ { // Test-case 3
+
+ { 0x9a, 0x33, 0xd1, 0x05, 0xce, 0x1b, 0xbe, 0x29 }, // challenge
+ { "0123456" }, // password
+ { 0x3f, 0x45, 0x9a, 0xd8, 0x60, 0xa1, 0xec, 0xe9, // response
+ 0x2d, 0x20, 0x4a, 0x26, 0xba, 0x3c, 0x5f, 0x08, // response
+ 0xde, 0x77, 0x35, 0x89, 0x40, 0x33, 0x04, 0x90 }, // response
+ },
+ { // Test-case 4
+
+ { 0xd3, 0xcb, 0x73, 0x6e, 0xb9, 0xdc, 0xe0, 0x1b }, // challenge
+ { "A" }, // a // password
+ { 0x1e, 0xc7, 0x15, 0x96, 0x0c, 0xf6, 0x2d, 0xb7, // response
+ 0x55, 0xd4, 0x1f, 0xf9, 0x9a, 0xae, 0xc2, 0x7d, // response
+ 0xab, 0x52, 0x50, 0xc8, 0xe9, 0x80, 0xae, 0x7d }, // response
+ },
+ { // Test-case 5
+
+ { 0x20, 0xed, 0x5a, 0x52, 0x75, 0x6a, 0x4c, 0xc0 }, // challenge
+ { "SUPER" }, // Super // password
+ { 0xe2, 0xeb, 0xd4, 0xbb, 0x0f, 0xe1, 0x0d, 0x90, // response
+ 0x0f, 0x9a, 0x86, 0x48, 0x85, 0xa3, 0x30, 0xba, // response
+ 0xa7, 0x9d, 0x21, 0xd8, 0x6d, 0xf6, 0xa7, 0xa4 }, // response
+ },
+ { // Test-case 6
+
+ { 0x9a, 0x8a, 0xee, 0x3d, 0xe5, 0xa1, 0x48, 0xb4 }, // challenge
+ { "SUPER003CALIFR" }, // Super003Califr // password
+ { 0x6d, 0x19, 0xd9, 0x56, 0x87, 0xc9, 0xbf, 0x70, // response
+ 0x3d, 0x24, 0x91, 0x01, 0x20, 0x29, 0x1f, 0xa5, // response
+ 0x12, 0xfc, 0xf0, 0xa4, 0xf3, 0xd7, 0x0a, 0x0b }, // response
+ }
+ };
+
+ CtPrint("\n\rRunning Lanman test-cases...");
+
+ // Run through each test-case
+ for (i=0; i < sizeof(testcases)/sizeof(LMTESTCASE); i++) {
+
+ LM_OWF_PASSWORD LmOwfPassword;
+
+ RtlCalculateLmOwfPassword(testcases[i].LmPassword, &LmOwfPassword);
+
+ RtlCalculateLmResponse(&testcases[i].LmChallenge, &LmOwfPassword, &LmResponse);
+
+ if (!CtEqualLmResponse(&LmResponse, &testcases[i].LmResponse)) {
+
+ CtPrint("\n\rTest-case %d failed...\n\r", i);
+ CtPrint("LmChallenge :\n\r");
+ CtPrintLmChallenge(&testcases[i].LmChallenge);
+ CtPrint("LmPassword :\n\r");
+ CtPrintLmPassword(testcases[i].LmPassword);
+ CtPrint("Expected LmResponse :\n\r");
+ CtPrintLmResponse(&testcases[i].LmResponse);
+ CtPrint("Actual LmResponse :\n\r");
+ CtPrintLmResponse(&LmResponse);
+
+ Success = FALSE;
+ }
+ }
+
+ CtPrint("Done\n\r");
+}
+
+ typedef struct {
+ WCHAR * UnicodePassword;
+ NT_OWF_PASSWORD NtOwfPassword;
+ } NTTESTCASE;
+
+VOID
+TestNTCompat(VOID)
+{
+ int i;
+
+
+ NTTESTCASE testcases[] = {
+
+#ifndef MIPS
+// LATER -- MIPS compiler can't handle multiple test-cases for some reason
+ { // Test-case 0
+
+ { L"a" }, // password
+ { 0x18, 0x6c, 0xb0, 0x91, 0x81, 0xe2, 0xc2, 0xec, // owf password
+ 0xaa, 0xc7, 0x68, 0xc4, 0x7c, 0x72, 0x99, 0x04 }, // owf password
+ },
+ { // Test-case 1
+
+ { NULL }, // password
+ { 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31, // owf password
+ 0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 }, // owf password
+ },
+ { // Test-case 2
+
+ { L"A" }, // password
+ { 0xc5, 0xdd, 0x1c, 0x2b, 0xc8, 0x71, 0x9c, 0x01, // owf password
+ 0xb2, 0x5b, 0x4e, 0xb2, 0x69, 0x2c, 0x9f, 0xee }, // owf password
+ },
+ { // Test-case 3
+
+ { L"Supercalifragilistic" }, // password
+ { 0x4d, 0x77, 0x81, 0x48, 0xe5, 0x7f, 0x51, 0x67, // owf password
+ 0xad, 0x68, 0xc5, 0xd7, 0xee, 0x33, 0x41, 0x77 }, // owf password
+ },
+#endif
+ { // Test-case 4
+
+ { L"SupercalifragilisticExpialidocious" }, // password
+ { 0xa8, 0xf2, 0x01, 0x3d, 0x85, 0x93, 0x76, 0x16, // owf password
+ 0x5a, 0xb3, 0x84, 0x8e, 0xd3, 0xae, 0x84, 0xb5 }, // owf password
+ }
+ };
+
+ CtPrint("\n\rRunning NT test-cases...");
+
+ // Run through each test-case
+ for (i=0; i < sizeof(testcases)/sizeof(NTTESTCASE); i++) {
+
+ NT_PASSWORD NtPassword;
+ NT_OWF_PASSWORD NtOwfPassword;
+
+ RtlInitUnicodeString(&NtPassword, testcases[i].UnicodePassword);
+
+ RtlCalculateNtOwfPassword(&NtPassword, &NtOwfPassword);
+
+ if (!CtEqualNtOwfPassword(&NtOwfPassword, &testcases[i].NtOwfPassword)) {
+
+ CtPrint("\n\rTest-case %d failed...\n\r", i);
+ CtPrint("NtPassword :\n\r");
+ CtPrintNtPassword(&NtPassword);
+ CtPrint("Expected NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&testcases[i].NtOwfPassword);
+ CtPrint("Actual NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&NtOwfPassword);
+
+ Success = FALSE;
+ }
+ }
+
+ CtPrint("Done\n\r");
+}
+
+
+VOID
+TestLMOWFCrypt(VOID)
+{
+ int i;
+ ULONG Seed;
+
+ GenerateSeed(&Seed);
+
+ CtPrint("\n\rTesting LMOWFPassword encryption/decryption with LMOWFPassword...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ LM_OWF_PASSWORD OwfPasswordData1;
+ LM_OWF_PASSWORD OwfPasswordData2;
+ LM_OWF_PASSWORD OwfPasswordKey;
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate two random OWFPasswords
+ GenerateRandomLmOwfPassword(&OwfPasswordData1, &Seed);
+ GenerateRandomLmOwfPassword(&OwfPasswordKey, &Seed);
+
+ // Encrypt and then decrypt block
+ RtlEncryptLmOwfPwdWithLmOwfPwd(&OwfPasswordData1, &OwfPasswordKey, &EncryptedOwfPassword);
+ RtlDecryptLmOwfPwdWithLmOwfPwd(&EncryptedOwfPassword, &OwfPasswordKey, &OwfPasswordData2);
+
+ if (!CtEqualLmOwfPassword(&OwfPasswordData1, &OwfPasswordData2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("LmOwfPasswordData :\n\r");
+ CtPrintLmOwfPassword(&OwfPasswordData1);
+ CtPrint("LmOwfPasswordKey :\n\r");
+ CtPrintLmOwfPassword(&OwfPasswordKey);
+ CtPrint("EncryptedLmOwfPassword :\n\r");
+ CtPrintEncryptedLmOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting LmOwfPassword :\n\r");
+ CtPrintLmOwfPassword(&OwfPasswordData2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+
+
+
+ CtPrint("\n\rTesting LMOWFPassword encryption/decryption with LM Session Key...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ LM_OWF_PASSWORD OwfPassword1;
+ LM_OWF_PASSWORD OwfPassword2;
+ LM_SESSION_KEY SessionKey;
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate a random OWFPassword and random session key
+ GenerateRandomLmOwfPassword(&OwfPassword1, &Seed);
+ GenerateRandomLmSessionKey(&SessionKey, &Seed);
+
+ // Encrypt and then decrypt OwfPassword
+ RtlEncryptLmOwfPwdWithLmSesKey(&OwfPassword1, &SessionKey, &EncryptedOwfPassword);
+ RtlDecryptLmOwfPwdWithLmSesKey(&EncryptedOwfPassword, &SessionKey, &OwfPassword2);
+
+ if (!CtEqualLmOwfPassword(&OwfPassword1, &OwfPassword2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("LmOwfPassword :\n\r");
+ CtPrintLmOwfPassword(&OwfPassword1);
+ CtPrint("LmSessionKey :\n\r");
+ CtPrintLmSessionKey(&SessionKey);
+ CtPrint("EncryptedLmOwfPassword :\n\r");
+ CtPrintEncryptedLmOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting LmOwfPassword :\n\r");
+ CtPrintLmOwfPassword(&OwfPassword2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+
+
+ CtPrint("\n\rTesting LMOWFPassword encryption/decryption with User Session Key...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ LM_OWF_PASSWORD OwfPassword1;
+ LM_OWF_PASSWORD OwfPassword2;
+ USER_SESSION_KEY UserSessionKey;
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate a random OWFPassword and random session key
+ GenerateRandomLmOwfPassword(&OwfPassword1, &Seed);
+ GenerateRandomUserSessionKey(&UserSessionKey, &Seed);
+
+ // Encrypt and then decrypt OwfPassword
+ RtlEncryptLmOwfPwdWithUserKey(&OwfPassword1, &UserSessionKey, &EncryptedOwfPassword);
+ RtlDecryptLmOwfPwdWithUserKey(&EncryptedOwfPassword, &UserSessionKey, &OwfPassword2);
+
+ if (!CtEqualLmOwfPassword(&OwfPassword1, &OwfPassword2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("LmOwfPassword :\n\r");
+ CtPrintLmOwfPassword(&OwfPassword1);
+ CtPrint("LmSessionKey :\n\r");
+ CtPrintUserSessionKey(&UserSessionKey);
+ CtPrint("EncryptedLmOwfPassword :\n\r");
+ CtPrintEncryptedLmOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting LmOwfPassword :\n\r");
+ CtPrintLmOwfPassword(&OwfPassword2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+
+
+
+ CtPrint("\n\rTesting LMOWFPassword encryption/decryption with Index...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ LM_OWF_PASSWORD OwfPassword1;
+ LM_OWF_PASSWORD OwfPassword2;
+ CRYPT_INDEX Index;
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate a random OWFPassword and index
+ GenerateRandomLmOwfPassword(&OwfPassword1, &Seed);
+ GenerateRandomCryptIndex(&Index, &Seed);
+
+ // Encrypt and then decrypt OwfPassword
+ RtlEncryptLmOwfPwdWithIndex(&OwfPassword1, &Index, &EncryptedOwfPassword);
+ RtlDecryptLmOwfPwdWithIndex(&EncryptedOwfPassword, &Index, &OwfPassword2);
+
+ if (!CtEqualLmOwfPassword(&OwfPassword1, &OwfPassword2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("LmOwfPassword :\n\r");
+ CtPrintLmOwfPassword(&OwfPassword1);
+ CtPrint("Index :\n\r");
+ CtPrintCryptIndex(&Index);
+ CtPrint("EncryptedLmOwfPassword :\n\r");
+ CtPrintEncryptedLmOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting LmOwfPassword :\n\r");
+ CtPrintLmOwfPassword(&OwfPassword2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+}
+
+
+VOID
+TestNTOWFCrypt(VOID)
+{
+ int i;
+ ULONG Seed;
+
+ GenerateSeed(&Seed);
+
+ CtPrint("\n\rTesting NTOWFPassword encryption/decryption with NTOWFPassword...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ NT_OWF_PASSWORD OwfPasswordData1;
+ NT_OWF_PASSWORD OwfPasswordData2;
+ NT_OWF_PASSWORD OwfPasswordKey;
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate two random OWFPasswords
+ GenerateRandomNtOwfPassword(&OwfPasswordData1, &Seed);
+ GenerateRandomNtOwfPassword(&OwfPasswordKey, &Seed);
+
+ // Encrypt and then decrypt block
+ RtlEncryptNtOwfPwdWithNtOwfPwd(&OwfPasswordData1, &OwfPasswordKey, &EncryptedOwfPassword);
+ RtlDecryptNtOwfPwdWithNtOwfPwd(&EncryptedOwfPassword, &OwfPasswordKey, &OwfPasswordData2);
+
+ if (!CtEqualNtOwfPassword(&OwfPasswordData1, &OwfPasswordData2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("NtOwfPasswordData :\n\r");
+ CtPrintNtOwfPassword(&OwfPasswordData1);
+ CtPrint("NtOwfPasswordKey :\n\r");
+ CtPrintNtOwfPassword(&OwfPasswordKey);
+ CtPrint("EncryptedNtOwfPassword :\n\r");
+ CtPrintEncryptedNtOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&OwfPasswordData2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+
+
+
+ CtPrint("\n\rTesting NTOWFPassword encryption/decryption with NT Session Key...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ NT_OWF_PASSWORD OwfPassword1;
+ NT_OWF_PASSWORD OwfPassword2;
+ NT_SESSION_KEY SessionKey;
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate a random OWFPassword and random session key
+ GenerateRandomNtOwfPassword(&OwfPassword1, &Seed);
+ GenerateRandomNtSessionKey(&SessionKey, &Seed);
+
+ // Encrypt and then decrypt OwfPassword
+ RtlEncryptNtOwfPwdWithNtSesKey(&OwfPassword1, &SessionKey, &EncryptedOwfPassword);
+ RtlDecryptNtOwfPwdWithNtSesKey(&EncryptedOwfPassword, &SessionKey, &OwfPassword2);
+
+ if (!CtEqualNtOwfPassword(&OwfPassword1, &OwfPassword2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&OwfPassword1);
+ CtPrint("NtSessionKey :\n\r");
+ CtPrintNtSessionKey(&SessionKey);
+ CtPrint("EncryptedNtOwfPassword :\n\r");
+ CtPrintEncryptedNtOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&OwfPassword2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+
+
+ CtPrint("\n\rTesting NTOWFPassword encryption/decryption with User Session Key...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ NT_OWF_PASSWORD OwfPassword1;
+ NT_OWF_PASSWORD OwfPassword2;
+ USER_SESSION_KEY UserSessionKey;
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate a random OWFPassword and random session key
+ GenerateRandomNtOwfPassword(&OwfPassword1, &Seed);
+ GenerateRandomUserSessionKey(&UserSessionKey, &Seed);
+
+ // Encrypt and then decrypt OwfPassword
+ RtlEncryptNtOwfPwdWithUserKey(&OwfPassword1, &UserSessionKey, &EncryptedOwfPassword);
+ RtlDecryptNtOwfPwdWithUserKey(&EncryptedOwfPassword, &UserSessionKey, &OwfPassword2);
+
+ if (!CtEqualNtOwfPassword(&OwfPassword1, &OwfPassword2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&OwfPassword1);
+ CtPrint("NtSessionKey :\n\r");
+ CtPrintUserSessionKey(&UserSessionKey);
+ CtPrint("EncryptedNtOwfPassword :\n\r");
+ CtPrintEncryptedNtOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&OwfPassword2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+
+
+
+ CtPrint("\n\rTesting NTOWFPassword encryption/decryption with Index...");
+
+ for (i=0; i < END_TO_END_ITERATIONS; i++) {
+
+ NT_OWF_PASSWORD OwfPassword1;
+ NT_OWF_PASSWORD OwfPassword2;
+ CRYPT_INDEX Index;
+ ENCRYPTED_NT_OWF_PASSWORD EncryptedOwfPassword;
+
+ // Generate a random OWFPassword and index
+ GenerateRandomNtOwfPassword(&OwfPassword1, &Seed);
+ GenerateRandomCryptIndex(&Index, &Seed);
+
+ // Encrypt and then decrypt OwfPassword
+ RtlEncryptNtOwfPwdWithIndex(&OwfPassword1, &Index, &EncryptedOwfPassword);
+ RtlDecryptNtOwfPwdWithIndex(&EncryptedOwfPassword, &Index, &OwfPassword2);
+
+ if (!CtEqualNtOwfPassword(&OwfPassword1, &OwfPassword2)) {
+ CtPrint("\n\rError - OWFPassword did not decrypt to same value as original\n\r");
+ CtPrint("NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&OwfPassword1);
+ CtPrint("Index :\n\r");
+ CtPrintCryptIndex(&Index);
+ CtPrint("EncryptedNtOwfPassword :\n\r");
+ CtPrintEncryptedNtOwfPassword(&EncryptedOwfPassword);
+ CtPrint("Resulting NtOwfPassword :\n\r");
+ CtPrintNtOwfPassword(&OwfPassword2);
+ Success = FALSE;
+ break;
+ }
+ }
+
+ CtPrint("Done.\n\r");
+}
+
+
+int _cdecl main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ Success = TRUE;
+
+ CtPrint("\n\r\n\r");
+ CtPrint("Beginning encryption component test\n\r");
+ CtPrint("===================================\n\r");
+ CtPrint("\n\r");
+
+ TestEndToEnd();
+ TestDataEndToEnd();
+ TestLMCompat();
+ TestNTCompat();
+ TestLMOWFCrypt();
+ TestNTOWFCrypt();
+
+ CtPrint("\n\rEncryption component test complete : ");
+
+ if (Success) {
+ CtPrint("PASSED\n\r");
+ } else {
+ CtPrint("FAILED\n\r");
+ }
+
+ return(0);
+}
+
+
+
diff --git a/private/lsa/crypt/test/ctmanual.c b/private/lsa/crypt/test/ctmanual.c
new file mode 100644
index 000000000..b700e7975
--- /dev/null
+++ b/private/lsa/crypt/test/ctmanual.c
@@ -0,0 +1,205 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ct1.c
+
+Abstract:
+
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+#include <string.h>
+#include <stdlib.h>
+#include "ctutil.h"
+
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ LM_OWF_PASSWORD LmOwfPassword1, LmOwfPassword2;
+ NT_OWF_PASSWORD NtOwfPassword;
+ ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
+ LM_CHALLENGE LmChallenge;
+ LM_RESPONSE LmResponse;
+ LM_SESSION_KEY LmSessionKey;
+ UNICODE_STRING UnicodeString;
+ STRING String;
+ CRYPT_INDEX Index;
+ USER_SESSION_KEY UserSessionKey;
+
+ if (argc < 2) {
+ CtPrint("Usage : ctmanual variation# arg1 arg2 ...\n\r");
+ return(0);
+ }
+
+ switch (atoi(argv[1])) {
+ case 0:
+
+ // LMPASSWORD -> LM_OWF_PASSWORD
+
+ CtPrint("Converting LMPASSWORD -> LM_OWF_PASSWORD\n\r");
+ CtPrint("LMPASSWORD : ");
+ CtPrintLmPassword(argv[2]);
+
+ RtlCalculateLmOwfPassword(argv[2], &LmOwfPassword1);
+
+ CtPrint("LM_OWF_PASSWORD :\n\r");
+ CtPrintLmOwfPassword(&LmOwfPassword1);
+ CtPrint("Done.\n\r\n\r");
+
+ break;
+
+ case 1:
+
+ // NT_PASSWORD -> NT_OWF_PASSWORD
+
+ CtPrint("Converting NT_PASSWORD -> NT_OWF_PASSWORD\n\r");
+ CtPrint("NT_PASSWORD : ");
+
+ if (argc >= 3) {
+ RtlInitString(&String, argv[2]);
+ } else {
+ RtlInitString(&String, NULL);
+ }
+ RtlAnsiStringToUnicodeString(&UnicodeString, &String, TRUE);
+
+ CtPrintNtPassword(&UnicodeString);
+
+ RtlCalculateNtOwfPassword(&UnicodeString, &NtOwfPassword);
+
+ CtPrint("NT_OWF_PASSWORD :\n\r");
+ CtPrintNtOwfPassword(&NtOwfPassword);
+ CtPrint("Done.\n\r\n\r");
+
+ RtlFreeUnicodeString(&UnicodeString);
+
+ break;
+
+ case 2:
+
+ // LM_CHALLENGE + LM_OWF_PASSWORD -> LM_RESPONSE
+
+ CtPrint("Converting LM_CHALLENGE + LM_OWF_PASSWORD -> LM_RESPONSE\n\r");
+
+ strncpy((PCHAR)&LmChallenge, argv[2], sizeof(LmChallenge));
+ strncpy((PCHAR)&LmOwfPassword1, argv[3], sizeof(LmOwfPassword1));
+
+ CtPrint("LM_CHALLENGE :\n\r");
+ CtPrintLmChallenge(&LmChallenge);
+ CtPrint("LM_OWF_PASSWORD :\n\r");
+ CtPrintLmOwfPassword(&LmOwfPassword1);
+
+ RtlCalculateLmResponse(&LmChallenge, &LmOwfPassword1, &LmResponse);
+
+ CtPrint("LM_RESPONSE :\n\r");
+ CtPrintLmResponse(&LmResponse);
+ CtPrint("Done.\n\r\n\r");
+
+ break;
+
+ case 3:
+
+ // Encrypt LM_OWF_PASSWORD with LM_OWF_PASSWORD
+
+ CtPrint("Encrypting LM_OWF_PASSWORD1 with LM_OWF_PASSWORD2\n\r");
+
+ strncpy((PCHAR)&LmOwfPassword1, argv[2], sizeof(LmOwfPassword1));
+ strncpy((PCHAR)&LmOwfPassword2, argv[3], sizeof(LmOwfPassword2));
+
+ CtPrint("LM_OWF_PASSWORD1 :\n\r");
+ CtPrintLmOwfPassword(&LmOwfPassword1);
+ CtPrint("LM_OWF_PASSWORD2 :\n\r");
+ CtPrintLmOwfPassword(&LmOwfPassword2);
+
+ RtlEncryptLmOwfPwdWithLmOwfPwd(&LmOwfPassword1, &LmOwfPassword2, &EncryptedLmOwfPassword);
+
+ CtPrint("ENCRYPTED_LM_OWF_PASSWORD :\n\r");
+ CtPrintEncryptedLmOwfPassword(&EncryptedLmOwfPassword);
+ CtPrint("Done.\n\r\n\r");
+
+ break;
+
+ case 4:
+
+ // Encrypt LM_OWF_PASSWORD with LM_SESSION_KEY
+
+ CtPrint("Encrypting LM_OWF_PASSWORD with LM_SESSION_KEY\n\r");
+
+ strncpy((PCHAR)&LmOwfPassword1, argv[2], sizeof(LmOwfPassword1));
+ strncpy((PCHAR)&LmSessionKey, argv[3], sizeof(LmSessionKey));
+
+ CtPrint("LM_OWF_PASSWORD : \n\r");
+ CtPrintLmOwfPassword(&LmOwfPassword1);
+ CtPrint("LM_SESSION_KEY : \n\r");
+ CtPrintLmSessionKey(&LmSessionKey);
+
+ RtlEncryptLmOwfPwdWithLmSesKey(&LmOwfPassword1, &LmSessionKey, &EncryptedLmOwfPassword);
+
+ CtPrint("ENCRYPTED_LM_OWF_PASSWORD :\n\r");
+ CtPrintEncryptedLmOwfPassword(&EncryptedLmOwfPassword);
+ CtPrint("Done.\n\r\n\r");
+
+ break;
+
+ case 5:
+
+ // Encrypt LM_OWF_PASSWORD with CRYPT_INDEX
+
+ CtPrint("Encrypting LM_OWF_PASSWORD with CRYPT_INDEX\n\r");
+
+ strncpy((PCHAR)&LmOwfPassword1, argv[2], sizeof(LmOwfPassword1));
+ strncpy((PCHAR)&Index, argv[3], sizeof(Index));
+
+ CtPrint("LM_OWF_PASSWORD : \n\r");
+ CtPrintLmOwfPassword(&LmOwfPassword1);
+ CtPrint("CRYPT_INDEX : ");
+ CtPrintCryptIndex(&Index);
+
+ RtlEncryptLmOwfPwdWithIndex(&LmOwfPassword1, &Index, &EncryptedLmOwfPassword);
+
+ CtPrint("ENCRYPTED_LM_OWF_PASSWORD :\n\r");
+ CtPrintEncryptedLmOwfPassword(&EncryptedLmOwfPassword);
+ CtPrint("Done.\n\r\n\r");
+
+ break;
+
+ case 6:
+
+ // LM_RESPONSE + LM_OWF_PASSWORD -> USER_SESSION_KEY
+
+ strncpy((PCHAR)&LmResponse, argv[2], sizeof(LmResponse));
+ strncpy((PCHAR)&LmOwfPassword1, argv[3], sizeof(LmOwfPassword1));
+
+ CtPrint("Converting LmResponse + LmOwfPassword -> UserSessionKey\n\r");
+ CtPrint("LmResponse : ");
+ CtPrintLmResponse(&LmResponse);
+ CtPrint("LmOwfPassword : ");
+ CtPrintLmOwfPassword(&LmOwfPassword1);
+
+ RtlCalculateUserSessionKeyLm(&LmResponse, &LmOwfPassword1, &UserSessionKey);
+
+ CtPrint("UserSessionKey :\n\r");
+ CtPrintUserSessionKey(&UserSessionKey);
+ CtPrint("Done.\n\r\n\r");
+
+ break;
+
+ default:
+ CtPrint("Unrecognised variation\n\r");
+ break;
+ }
+
+ return(0);
+}
diff --git a/private/lsa/crypt/test/ctutil.c b/private/lsa/crypt/test/ctutil.c
new file mode 100644
index 000000000..05d1a8209
--- /dev/null
+++ b/private/lsa/crypt/test/ctutil.c
@@ -0,0 +1,384 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ctutil.c
+
+Abstract:
+
+ Useful functions used by component test routines
+
+Author:
+
+ David Chalmers (Davidc) 10-21-91
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <crypt.h>
+#include <string.h>
+#include <stdlib.h>
+#include "ctutil.h"
+
+#define ISANSI(c) ( ((c >= 'a') && (c <= 'z')) || \
+ ((c >= 'A') && (c <= 'Z')) || \
+ (c >= 32) \
+ )
+
+VOID
+CtPrintClearBlocks(
+ PCLEAR_BLOCK ClearBlock,
+ ULONG BlockCount)
+{
+
+ ULONG i, j;
+
+ for (i=0; i < BlockCount; i++ ) {
+ for (j=0; j < CLEAR_BLOCK_LENGTH; j++) {
+ CHAR c = ClearBlock[i].data[j];
+ if (ISANSI(c)) {
+ CtPrint(" %c", c);
+ } else {
+ CtPrint(" ");
+ }
+ }
+ CtPrint(" ");
+ }
+
+ CtPrint("\n\r");
+
+ for (i=0; i < BlockCount; i++ ) {
+ for (j=0; j < CLEAR_BLOCK_LENGTH; j++) {
+ CtPrint("%2.2x", (int)ClearBlock[i].data[j] & 0xff);
+ }
+ CtPrint(" ");
+ }
+
+ CtPrint("\n\r");
+}
+
+
+BOOLEAN
+CtEqualClearBlock(
+ PCLEAR_BLOCK ClearBlock1,
+ PCLEAR_BLOCK ClearBlock2)
+{
+
+ ULONG j;
+
+ for (j=0; j<8; j++) {
+ if (ClearBlock1->data[j] != ClearBlock2->data[j]) {
+ return(FALSE);
+ }
+ }
+ return(TRUE);
+}
+
+
+VOID
+CtPrintBlockKeys(
+ PBLOCK_KEY BlockKey,
+ ULONG KeyCount)
+{
+
+ ULONG i, j;
+
+ for (i=0; i < KeyCount; i++ ) {
+ for (j=0; j < BLOCK_KEY_LENGTH; j++) {
+ CHAR c = BlockKey[i].data[j];
+ if (ISANSI(c)) {
+ CtPrint(" %c", c);
+ } else {
+ CtPrint(" ");
+ }
+ }
+ CtPrint(" ");
+ }
+
+ CtPrint("\n\r");
+
+ for (i=0; i < KeyCount; i++ ) {
+ for (j=0; j < BLOCK_KEY_LENGTH; j++) {
+ CtPrint("%2.2x", (int)BlockKey[i].data[j] & 0xff);
+ }
+ CtPrint(" ");
+ }
+
+ CtPrint("\n\r");
+}
+
+
+VOID
+CtPrintCypherBlocks(
+ PCYPHER_BLOCK CypherBlock,
+ ULONG BlockCount)
+{
+ CtPrintClearBlocks((PCLEAR_BLOCK)CypherBlock, BlockCount);
+}
+
+
+BOOLEAN
+CtEqualCypherBlock(
+ PCYPHER_BLOCK CypherBlock1,
+ PCYPHER_BLOCK CypherBlock2)
+{
+ return(CtEqualClearBlock((PCLEAR_BLOCK)CypherBlock1, (PCLEAR_BLOCK)CypherBlock2));
+}
+
+
+VOID
+CtPrintClearData(
+ PCLEAR_DATA ClearData)
+{
+ CtPrint("Data length = %ld\n\r", ClearData->Length);
+ CtPrintClearBlocks((PCLEAR_BLOCK)(ClearData->Buffer),
+ (ClearData->Length + (CLEAR_BLOCK_LENGTH-1)) / CLEAR_BLOCK_LENGTH);
+}
+
+
+BOOLEAN
+CtEqualClearData(
+ PCLEAR_DATA ClearData1,
+ PCLEAR_DATA ClearData2)
+{
+ if (ClearData1->Length != ClearData2->Length) {
+ return(FALSE);
+ }
+
+ return((BOOLEAN)(RtlCompareMemory(ClearData1->Buffer,
+ ClearData2->Buffer,
+ ClearData1->Length)
+ == ClearData1->Length));
+}
+
+
+VOID
+CtPrintCypherData(
+ PCYPHER_DATA CypherData)
+{
+ CtPrintClearData((PCLEAR_DATA)CypherData);
+}
+
+
+BOOLEAN
+CtEqualCypherData(
+ PCYPHER_DATA CypherData1,
+ PCYPHER_DATA CypherData2)
+{
+ return(CtEqualClearData((PCLEAR_DATA)CypherData1, (PCLEAR_DATA)CypherData2));
+}
+
+
+VOID
+CtPrintDataKey(
+ PDATA_KEY Key)
+{
+ ULONG BlockKeys;
+
+ BlockKeys = Key->Length / BLOCK_KEY_LENGTH;
+
+ CtPrint("DataKey length = %ld\n\r", Key->Length);
+
+ CtPrintBlockKeys((PBLOCK_KEY)(Key->Buffer), BlockKeys);
+}
+
+
+VOID
+CtPrintLmPassword(
+ PLM_PASSWORD LmPassword)
+{
+ CtPrint("%s\n\r", LmPassword);
+}
+
+
+BOOLEAN
+CtEqualLmPassword(
+ PLM_PASSWORD LmPassword1,
+ PLM_PASSWORD LmPassword2)
+{
+ return (BOOLEAN)(strcmp(LmPassword1, LmPassword2) == 0);
+}
+
+
+VOID
+CtPrintNtPassword(
+ PNT_PASSWORD NtPassword)
+{
+ CtPrint("%wZ\n\r", NtPassword);
+}
+
+
+BOOLEAN
+CtEqualNtPassword(
+ PNT_PASSWORD NtPassword1,
+ PNT_PASSWORD NtPassword2)
+{
+ return(RtlEqualUnicodeString(NtPassword1, NtPassword2, FALSE));
+}
+
+
+VOID
+CtPrintLmOwfPassword(
+ PLM_OWF_PASSWORD LmOwfPassword)
+{
+ CtPrintCypherBlocks((PCYPHER_BLOCK)LmOwfPassword, 2);
+}
+
+
+BOOLEAN
+CtEqualLmOwfPassword(
+ PLM_OWF_PASSWORD LmOwfPassword1,
+ PLM_OWF_PASSWORD LmOwfPassword2)
+{
+ int i;
+
+ for (i=0; i<2; i++) {
+ if (!CtEqualCypherBlock(&LmOwfPassword1->data[i], &LmOwfPassword2->data[i])) {
+ return(FALSE);
+ }
+ }
+ return(TRUE);
+}
+
+
+VOID
+CtPrintNtOwfPassword(
+ PNT_OWF_PASSWORD NtOwfPassword)
+{
+ CtPrintCypherBlocks((PCYPHER_BLOCK)NtOwfPassword, 2);
+}
+
+
+BOOLEAN
+CtEqualNtOwfPassword(
+ PNT_OWF_PASSWORD NtOwfPassword1,
+ PNT_OWF_PASSWORD NtOwfPassword2)
+{
+ return(CtEqualLmOwfPassword((PLM_OWF_PASSWORD)NtOwfPassword1, (PLM_OWF_PASSWORD)NtOwfPassword2));
+}
+
+
+VOID
+CtPrintEncryptedLmOwfPassword(
+ PENCRYPTED_LM_OWF_PASSWORD LMENCRYPTEDOWFPassword)
+{
+ CtPrintCypherBlocks((PCYPHER_BLOCK)LMENCRYPTEDOWFPassword, 2);
+}
+
+
+BOOLEAN
+CtEqualEncryptedLmOwfPassword(
+ PENCRYPTED_LM_OWF_PASSWORD LMENCRYPTEDOWFPassword1,
+ PENCRYPTED_LM_OWF_PASSWORD LMENCRYPTEDOWFPassword2)
+{
+ int i;
+
+ for (i=0; i<2; i++) {
+ if (!CtEqualCypherBlock(&LMENCRYPTEDOWFPassword1->data[i], &LMENCRYPTEDOWFPassword2->data[i])) {
+ return(FALSE);
+ }
+ }
+ return(TRUE);
+}
+
+
+VOID
+CtPrintEncryptedNtOwfPassword(
+ PENCRYPTED_NT_OWF_PASSWORD NTENCRYPTEDOWFPassword)
+{
+ CtPrintCypherBlocks((PCYPHER_BLOCK)NTENCRYPTEDOWFPassword, 2);
+}
+
+
+BOOLEAN
+CtEqualEncryptedNtOwfPassword(
+ PENCRYPTED_NT_OWF_PASSWORD NTENCRYPTEDOWFPassword1,
+ PENCRYPTED_NT_OWF_PASSWORD NTENCRYPTEDOWFPassword2)
+{
+ return(CtEqualEncryptedLmOwfPassword((PENCRYPTED_LM_OWF_PASSWORD)NTENCRYPTEDOWFPassword1, (PENCRYPTED_LM_OWF_PASSWORD)NTENCRYPTEDOWFPassword2));
+}
+
+
+VOID
+CtPrintLmChallenge(
+ PLM_CHALLENGE LmChallenge)
+{
+ CtPrintClearBlocks((PCLEAR_BLOCK)LmChallenge, 1);
+}
+
+
+BOOLEAN
+CtEqualLmChallenge(
+ PLM_CHALLENGE LmChallenge1,
+ PLM_CHALLENGE LmChallenge2)
+{
+ return (CtEqualClearBlock(LmChallenge1, LmChallenge2));
+}
+
+
+VOID
+CtPrintLmResponse(
+ PLM_RESPONSE LmResponse)
+{
+ CtPrintCypherBlocks((PCYPHER_BLOCK)LmResponse, 3);
+}
+
+
+BOOLEAN
+CtEqualLmResponse(
+ PLM_RESPONSE LmResponse1,
+ PLM_RESPONSE LmResponse2)
+{
+ int i;
+
+ for (i=0; i<3; i++) {
+ if (!CtEqualCypherBlock(&LmResponse1->data[i], &LmResponse2->data[i])) {
+ return(FALSE);
+ }
+ }
+ return(TRUE);
+}
+
+
+VOID
+CtPrintLmSessionKey(
+ PLM_SESSION_KEY LmSessionKey)
+{
+ ASSERT(sizeof(LM_SESSION_KEY) == sizeof(LM_CHALLENGE));
+
+ CtPrintLmChallenge((PLM_CHALLENGE)LmSessionKey);
+}
+
+
+VOID
+CtPrintNtSessionKey(
+ PLM_SESSION_KEY NtSessionKey)
+{
+ ASSERT(sizeof(LM_SESSION_KEY) == sizeof(NT_SESSION_KEY));
+
+ CtPrintLmSessionKey((PLM_SESSION_KEY)NtSessionKey);
+}
+
+
+VOID
+CtPrintUserSessionKey(
+ PUSER_SESSION_KEY UserSessionKey)
+{
+ CtPrintCypherBlocks((PCYPHER_BLOCK)UserSessionKey, 1);
+}
+
+
+VOID
+CtPrintCryptIndex(
+ PCRYPT_INDEX Index)
+{
+ CtPrint("0x%x\n\r", *Index);
+}
+
+
+
diff --git a/private/lsa/crypt/test/ctutil.h b/private/lsa/crypt/test/ctutil.h
new file mode 100644
index 000000000..7d8ce0638
--- /dev/null
+++ b/private/lsa/crypt/test/ctutil.h
@@ -0,0 +1,161 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ctutil.h
+
+Abstract:
+
+ This module contains the public data structures and API definitions
+ needed to utilize the encryption component test library
+
+
+Author:
+
+ David Chalmers (Davidc) 21-October-1991
+
+Revision History:
+
+--*/
+
+
+#include "stdio.h"
+#define CtPrint printf
+
+VOID
+CtPrintClearBlocks(
+ PCLEAR_BLOCK ClearBlock,
+ ULONG BlockCount);
+
+BOOLEAN
+CtEqualClearBlock(
+ PCLEAR_BLOCK ClearBlock1,
+ PCLEAR_BLOCK ClearBlock2);
+
+VOID
+CtPrintBlockKeys(
+ PBLOCK_KEY BlockKey,
+ ULONG KeyCount);
+
+VOID
+CtPrintCypherBlocks(
+ PCYPHER_BLOCK CypherBlock,
+ ULONG BlockCount);
+
+BOOLEAN
+CtEqualCypherBlock(
+ PCYPHER_BLOCK CypherBlock1,
+ PCYPHER_BLOCK CypherBlock2);
+
+VOID
+CtPrintClearData(
+ PCLEAR_DATA ClearData);
+
+BOOLEAN
+CtEqualClearData(
+ PCLEAR_DATA ClearData1,
+ PCLEAR_DATA ClearData2);
+
+VOID
+CtPrintCypherData(
+ PCYPHER_DATA CypherData);
+
+BOOLEAN
+CtEqualCypherData(
+ PCYPHER_DATA CypherData1,
+ PCYPHER_DATA CypherData2);
+
+VOID
+CtPrintDataKey(
+ PDATA_KEY Key);
+
+VOID
+CtPrintLmPassword(
+ PLM_PASSWORD LmPassword);
+
+BOOLEAN
+CtEqualLmPassword(
+ PLM_PASSWORD LmPassword1,
+ PLM_PASSWORD LmPassword2);
+
+VOID
+CtPrintNtPassword(
+ PNT_PASSWORD NtPassword);
+
+BOOLEAN
+CtEqualNtPassword(
+ PNT_PASSWORD NtPassword1,
+ PNT_PASSWORD NtPassword2);
+
+VOID
+CtPrintLmOwfPassword(
+ PLM_OWF_PASSWORD LmOwfPassword);
+
+BOOLEAN
+CtEqualLmOwfPassword(
+ PLM_OWF_PASSWORD LmOwfPassword1,
+ PLM_OWF_PASSWORD LmOwfPassword2);
+
+VOID
+CtPrintNtOwfPassword(
+ PNT_OWF_PASSWORD NtOwfPassword);
+
+BOOLEAN
+CtEqualNtOwfPassword(
+ PNT_OWF_PASSWORD NtOwfPassword1,
+ PNT_OWF_PASSWORD NtOwfPassword2);
+
+VOID
+CtPrintEncryptedLmOwfPassword(
+ PENCRYPTED_LM_OWF_PASSWORD LMENCRYPTEDOWFPassword);
+
+BOOLEAN
+CtEqualEncryptedLmOwfPassword(
+ PENCRYPTED_LM_OWF_PASSWORD LMENCRYPTEDOWFPassword1,
+ PENCRYPTED_LM_OWF_PASSWORD LMENCRYPTEDOWFPassword2);
+
+VOID
+CtPrintEncryptedNtOwfPassword(
+ PENCRYPTED_NT_OWF_PASSWORD NTENCRYPTEDOWFPassword);
+
+BOOLEAN
+CtEqualEncryptedNtOwfPassword(
+ PENCRYPTED_NT_OWF_PASSWORD NTENCRYPTEDOWFPassword1,
+ PENCRYPTED_NT_OWF_PASSWORD NTENCRYPTEDOWFPassword2);
+
+VOID
+CtPrintLmChallenge(
+ PLM_CHALLENGE LmChallenge);
+
+BOOLEAN
+CtEqualLmChallenge(
+ PLM_CHALLENGE LmChallenge1,
+ PLM_CHALLENGE LmChallenge2);
+
+VOID
+CtPrintLmResponse(
+ PLM_RESPONSE LmResponse);
+
+BOOLEAN
+CtEqualLmResponse(
+ PLM_RESPONSE LmResponse1,
+ PLM_RESPONSE LmResponse2);
+
+VOID
+CtPrintLmSessionKey(
+ PLM_SESSION_KEY LmSessionKey);
+
+VOID
+CtPrintNtSessionKey(
+ PLM_SESSION_KEY NtSessionKey);
+
+VOID
+CtPrintUserSessionKey(
+ PUSER_SESSION_KEY UserSessionKey);
+
+VOID
+CtPrintCryptIndex(
+ PCRYPT_INDEX Index);
+
diff --git a/private/lsa/crypt/test/makefile b/private/lsa/crypt/test/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/crypt/test/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/crypt/test/sources b/private/lsa/crypt/test/sources
new file mode 100644
index 000000000..a16f65ab9
--- /dev/null
+++ b/private/lsa/crypt/test/sources
@@ -0,0 +1,44 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+
+!ENDIF
+
+MAJORCOMP=crypt
+MINORCOMP=ctutil
+
+TARGETNAME=ctutil
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+TARGETLIBS=
+
+INCLUDES=$(BASEDIR)\private\inc
+
+SOURCES=ctutil.c
+
+UMTYPE=console
+UMTEST=ctmanual
+UMAPPL=ctauto
+UMLIBS=obj\*\ctutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\sys003.lib
+
+
diff --git a/private/lsa/dirs b/private/lsa/dirs
new file mode 100644
index 000000000..906466815
--- /dev/null
+++ b/private/lsa/dirs
@@ -0,0 +1,33 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=client \
+ uclient \
+ server \
+ common \
+ msprivs \
+ msv1_0 \
+ crypt \
+
+
+OPTIONAL_DIRS= \
+ tmpv1_0
diff --git a/private/lsa/inc/aup.h b/private/lsa/inc/aup.h
new file mode 100644
index 000000000..9a4e7dabb
--- /dev/null
+++ b/private/lsa/inc/aup.h
@@ -0,0 +1,217 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ aup.h
+
+Abstract:
+
+ Local Security Authority definitions that are related to AUTHENTICATION
+ services and are shared between the LSA server and LSA client stubs
+
+Author:
+
+ Jim Kelly (JimK) 20-Feb-1991
+
+Revision History:
+
+--*/
+
+#ifndef _AUP_
+#define _AUP_
+
+
+#define LSAP_MAX_LOGON_PROC_NAME_LENGTH 127
+#define LSAP_MAX_PACKAGE_NAME_LENGTH 127
+
+
+//
+// Used for connecting to the LSA authentiction port.
+//
+
+typedef struct _LSAP_AU_REGISTER_CONNECT_INFO {
+ NTSTATUS CompletionStatus;
+ LSA_OPERATIONAL_MODE SecurityMode;
+ ULONG LogonProcessNameLength;
+ CHAR LogonProcessName[LSAP_MAX_PACKAGE_NAME_LENGTH+1];
+} LSAP_AU_REGISTER_CONNECT_INFO, *PLSAP_AU_REGISTER_CONNECT_INFO;
+
+
+
+
+
+//
+// Message formats used by clients of the local security authority.
+// Note that:
+//
+// LsaFreeReturnBuffer() does not result in a call to the server.
+//
+// LsaRegisterLogonProcess() is handled completely by the
+// LPC port connection, and requires no API number.
+//
+// DeRegister Logon Process doesn't have a call-specific structure.
+//
+
+typedef enum _LSAP_AU_API_NUMBER {
+ LsapAuLookupPackageApi,
+ LsapAuLogonUserApi,
+ LsapAuCallPackageApi,
+ LsapAuDeregisterLogonProcessApi,
+ LsapAuMaxApiNumber
+} LSAP_AU_API_NUMBER, *PLSAP_AU_API_NUMBER;
+
+
+//
+// Each API results in a data structure containing the parameters
+// of that API being transmitted to the LSA server. This data structure
+// (LSAP_API_MESSAGE) has a common header and a body which is dependent
+// upon the type of call being made. The following data structures are
+// the call-specific body formats.
+//
+
+typedef struct _LSAP_LOOKUP_PACKAGE_ARGS {
+ ULONG AuthenticationPackage; // OUT parameter
+ ULONG PackageNameLength;
+ CHAR PackageName[LSAP_MAX_PACKAGE_NAME_LENGTH+1];
+} LSAP_LOOKUP_PACKAGE_ARGS, *PLSAP_LOOKUP_PACKAGE_ARGS;
+
+typedef struct _LSAP_LOGON_USER_ARGS {
+ STRING OriginName;
+ SECURITY_LOGON_TYPE LogonType;
+ ULONG AuthenticationPackage;
+ PVOID AuthenticationInformation;
+ ULONG AuthenticationInformationLength;
+ ULONG LocalGroupsCount;
+ PTOKEN_GROUPS LocalGroups;
+ TOKEN_SOURCE SourceContext;
+ NTSTATUS SubStatus; // OUT parameter
+ PVOID ProfileBuffer; // OUT parameter
+ ULONG ProfileBufferLength; // OUT parameter
+ ULONG DummySpacer; // Spacer to force LUID to 8 byte alignment
+ LUID LogonId; // OUT parameter
+ HANDLE Token; // OUT parameter
+ QUOTA_LIMITS Quotas; // OUT parameter
+} LSAP_LOGON_USER_ARGS, *PLSAP_LOGON_USER_ARGS;
+
+typedef struct _LSAP_CALL_PACKAGE_ARGS {
+ ULONG AuthenticationPackage;
+ PVOID ProtocolSubmitBuffer;
+ ULONG SubmitBufferLength;
+ NTSTATUS ProtocolStatus; // OUT parameter
+ PVOID ProtocolReturnBuffer; // OUT parameter
+ ULONG ReturnBufferLength; // OUT parameter
+} LSAP_CALL_PACKAGE_ARGS, *PLSAP_CALL_PACKAGE_ARGS;
+
+
+
+
+//
+// This is the message that gets sent for every LSA LPC call.
+//
+
+typedef struct _LSAP_AU_API_MESSAGE {
+ PORT_MESSAGE PortMessage;
+ union {
+ LSAP_AU_REGISTER_CONNECT_INFO ConnectionRequest;
+ struct {
+ LSAP_AU_API_NUMBER ApiNumber;
+ NTSTATUS ReturnedStatus;
+ union {
+ LSAP_LOOKUP_PACKAGE_ARGS LookupPackage;
+ LSAP_LOGON_USER_ARGS LogonUser;
+ LSAP_CALL_PACKAGE_ARGS CallPackage;
+ } Arguments;
+ };
+ };
+} LSAP_AU_API_MESSAGE, *PLSAP_AU_API_MESSAGE;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Private Interface definitions available for use by Microsoft //
+// authentication packages //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+typedef NTSTATUS
+(*PLSAP_GET_OPERATIONAL_MODE) (
+ OUT PLSA_OPERATIONAL_MODE OperationalMode
+ );
+
+typedef NTSTATUS
+(*PLSAP_GET_PRIMARY_DOMAIN) (
+ OUT PBOOLEAN PrimaryDomainDefined,
+ OUT PSTRING *PrimaryDomain
+ );
+
+typedef NTSTATUS
+(*PLSAP_IMPERSONATE_CLIENT) (
+ IN PLSA_CLIENT_REQUEST ClientRequest
+ );
+
+
+
+
+//
+// Dispatch table of private LSA services which are available to
+// Microsoft authentication packages.
+//
+
+typedef struct _LSAP_PRIVATE_LSA_SERVICES {
+ PLSAP_GET_OPERATIONAL_MODE GetOperationalMode;
+ PLSAP_GET_PRIMARY_DOMAIN GetPrimaryDomain;
+ PLSAP_IMPERSONATE_CLIENT ImpersonateClient;
+} LSAP_PRIVATE_LSA_SERVICES, *PLSAP_PRIVATE_LSA_SERVICES;
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Interface definitions of services provided by Microsoft //
+// authentication packages that aren't part of the standard interface //
+// definition. //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+
+//
+// Routine names
+//
+// The routines provided by the DLL must be assigned the following names
+// so that their addresses can be retrieved when the DLL is loaded.
+//
+
+#define LSAP_AP_NAME_MS_INITIALIZE "LsaApMsInitialize\0"
+
+
+//
+// Routine templates
+//
+
+
+typedef VOID
+(*PLSA_AP_MS_INITIALIZE) (
+ IN PLSAP_PRIVATE_LSA_SERVICES PrivateLsaApi
+ );
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Internal (private) client-side routine definitions //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+#endif // _AUP_
diff --git a/private/lsa/inc/cr.h b/private/lsa/inc/cr.h
new file mode 100644
index 000000000..ae4d5bc71
--- /dev/null
+++ b/private/lsa/inc/cr.h
@@ -0,0 +1,131 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ cr.h
+
+Abstract:
+
+ Local Security Authority - Encryption Routine Definitions
+
+ NOTE: This file is included via lsacomp.h. It should
+ not be included directly.
+
+Author:
+
+ Scott Birrell (ScottBi) December 13, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+//
+// Max encryption Key Length
+//
+
+#define LSAP_CR_MAX_CIPHER_KEY_LENGTH (0x00000010L)
+
+//
+// Cipher Key Structure
+//
+
+typedef struct _LSAP_CR_CIPHER_KEY {
+
+ ULONG Length;
+ ULONG MaximumLength;
+ PUCHAR Buffer;
+
+} LSAP_CR_CIPHER_KEY, *PLSAP_CR_CIPHER_KEY;
+
+
+//
+// Clear value structure
+//
+
+typedef struct _LSAP_CR_CLEAR_VALUE {
+
+ ULONG Length;
+ ULONG MaximumLength;
+ PUCHAR Buffer;
+
+} LSAP_CR_CLEAR_VALUE, *PLSAP_CR_CLEAR_VALUE;
+
+//
+// Two-way encrypted value structure in Self-relative form. This
+// is just like a String.
+//
+
+typedef struct _LSAP_CR_CIPHER_VALUE {
+
+ ULONG Length;
+ ULONG MaximumLength;
+ PUCHAR Buffer;
+
+} LSAP_CR_CIPHER_VALUE, *PLSAP_CR_CIPHER_VALUE;
+
+
+NTSTATUS
+LsapCrClientGetSessionKey(
+ IN LSA_HANDLE ObjectHandle,
+ OUT PLSAP_CR_CIPHER_KEY *SessionKey
+ );
+
+NTSTATUS
+LsapCrServerGetSessionKey(
+ IN LSA_HANDLE ObjectHandle,
+ OUT PLSAP_CR_CIPHER_KEY *SessionKey
+ );
+
+NTSTATUS
+LsapCrEncryptValue(
+ IN PLSAP_CR_CLEAR_VALUE ClearValue,
+ IN PLSAP_CR_CIPHER_KEY CipherKey,
+ OUT PLSAP_CR_CIPHER_VALUE *CipherValue
+ );
+
+NTSTATUS
+LsapCrDecryptValue(
+ IN PLSAP_CR_CIPHER_VALUE CipherValue,
+ IN PLSAP_CR_CIPHER_KEY CipherKey,
+ OUT PLSAP_CR_CLEAR_VALUE *ClearValue
+ );
+
+VOID
+LsapCrFreeMemoryValue(
+ IN PVOID MemoryValue
+ );
+
+VOID
+LsapCrUnicodeToClearValue(
+ IN PUNICODE_STRING UnicodeString,
+ OUT PLSAP_CR_CLEAR_VALUE ClearValue
+ );
+
+VOID
+LsapCrClearValueToUnicode(
+ IN PLSAP_CR_CLEAR_VALUE ClearValue,
+ OUT PUNICODE_STRING UnicodeString
+ );
+
+#define LsapCrRtlEncryptData(ClearData, CipherKey, CipherData) \
+ ( \
+ RtlEncryptData( \
+ (PCLEAR_DATA) ClearData, \
+ (PDATA_KEY) CipherKey, \
+ (PCYPHER_DATA) CipherData \
+ ) \
+ )
+
+
+#define LsapCrRtlDecryptData(ClearData, CipherKey, CipherData) \
+ ( \
+ RtlDecryptData( \
+ (PCLEAR_DATA) ClearData, \
+ (PDATA_KEY) CipherKey, \
+ (PCYPHER_DATA) CipherData \
+ ) \
+ )
diff --git a/private/lsa/inc/lsacomp.h b/private/lsa/inc/lsacomp.h
new file mode 100644
index 000000000..7c9aded73
--- /dev/null
+++ b/private/lsa/inc/lsacomp.h
@@ -0,0 +1,65 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsacomp.h
+
+Abstract:
+
+ Local Security Authority - Main Include File for Client/Server Common
+ Definitions.
+
+
+ This file contains #includes for each of the files that contain
+ private LSA definitions that are common to the client/server side.
+
+Author:
+
+ Scott Birrell (ScottBi) February 19, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+//
+// The following come from \nt\public\sdk\inc
+//
+
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntregapi.h>
+#include <rpc.h>
+#include <msaudite.h>
+
+//
+// The following come from \nt\public\sdk\inc\crt
+//
+
+#include <stdlib.h>
+
+//
+// The following come from \nt\private\inc
+//
+
+#include <seopaque.h>
+#include <ntrmlsa.h>
+#include <ntrpcp.h>
+#include <crypt.h>
+
+//
+// The following come from \nt\private\lsa\inc
+//
+
+#include <cr.h>
+#include <lsaprtl.h>
+
diff --git a/private/lsa/inc/lsaprtl.h b/private/lsa/inc/lsaprtl.h
new file mode 100644
index 000000000..5e922d6d6
--- /dev/null
+++ b/private/lsa/inc/lsaprtl.h
@@ -0,0 +1,151 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsaprtl.h
+
+Abstract:
+
+ Local Security Authority - Temporary Rtl Routine Definitions.
+
+ This file contains definitions for routines used in the LSA that could
+ be made into Rtl routines. They have been written in general purpose
+ form with this in mind - the only exception to thisa is that their names
+ have Lsap prefixes to indicate that they are currently used only by the
+ LSA.
+
+ Scott Birrell (ScottBi) March 26, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+// Options for LsapRtlAddPrivileges
+
+#define RTL_COMBINE_PRIVILEGE_ATTRIBUTES ((ULONG) 0x00000001L)
+#define RTL_SUPERSEDE_PRIVILEGE_ATTRIBUTES ((ULONG) 0x00000002L)
+
+NTSTATUS
+LsapRtlAddPrivileges(
+ IN PPRIVILEGE_SET ExistingPrivileges,
+ IN PPRIVILEGE_SET PrivilegesToAdd,
+ IN OPTIONAL PPRIVILEGE_SET UpdatedPrivileges,
+ IN PULONG UpdatedPrivilegesSize,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapRtlRemovePrivileges(
+ IN PPRIVILEGE_SET ExistingPrivileges,
+ IN PPRIVILEGE_SET PrivilegesToRemove,
+ IN OPTIONAL PPRIVILEGE_SET UpdatedPrivileges,
+ IN PULONG UpdatedPrivilegesSize
+ );
+
+PLUID_AND_ATTRIBUTES
+LsapRtlGetPrivilege(
+ IN PLUID_AND_ATTRIBUTES Privilege,
+ IN PPRIVILEGE_SET Privileges
+ );
+
+NTSTATUS
+LsapRtlLookupKnownPrivilegeValue(
+ IN PSTRING PrivilegeName,
+ OUT PLUID Value
+ );
+
+NTSTATUS
+LsapRtlValidatePrivilegeSet(
+ IN PPRIVILEGE_SET Privileges
+ );
+
+BOOLEAN
+LsapRtlIsValidPrivilege(
+ IN PLUID_AND_ATTRIBUTES Privilege
+ );
+
+NTSTATUS
+LsapRtlCopyUnicodeString(
+ IN PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ );
+
+BOOLEAN
+LsapRtlPrefixSid(
+ IN PSID PrefixSid,
+ IN PSID Sid
+ );
+
+BOOLEAN
+LsapRtlPrefixName(
+ IN PUNICODE_STRING PrefixName,
+ IN PUNICODE_STRING Name
+ );
+
+LONG
+LsapRtlFindCharacterInUnicodeString(
+ IN PUNICODE_STRING InputString,
+ IN PUNICODE_STRING Character,
+ IN BOOLEAN CaseInsensitive
+ );
+
+VOID
+LsapRtlSplitNames(
+ IN PUNICODE_STRING Names,
+ IN ULONG Count,
+ IN PUNICODE_STRING Separator,
+ OUT PUNICODE_STRING PrefixNames,
+ OUT PUNICODE_STRING SuffixNames
+ );
+
+NTSTATUS
+LsapRtlCopyUnicodeString(
+ OUT PUNICODE_STRING OutputString,
+ IN PUNICODE_STRING InputString,
+ BOOLEAN AllocateMemory
+ );
+
+VOID
+LsapRtlSetSecurityAccessMask(
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PACCESS_MASK DesiredAccess
+ );
+
+VOID
+LsapRtlQuerySecurityAccessMask(
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PACCESS_MASK DesiredAccess
+ );
+
+NTSTATUS
+LsapRtlSidToUnicodeRid(
+ IN PSID Sid,
+ OUT PUNICODE_STRING UnicodeRid
+ );
+
+NTSTATUS
+LsapRtlPrivilegeSetToLuidAndAttributes(
+ IN OPTIONAL PPRIVILEGE_SET PrivilegeSet,
+ OUT PULONG PrivilegeCount,
+ OUT PLUID_AND_ATTRIBUTES *LuidAndAttributes
+ );
+
+NTSTATUS
+LsapRtlWellKnownPrivilegeCheck(
+ IN PVOID ObjectHandle,
+ IN BOOLEAN ImpersonateClient,
+ IN ULONG PrivilegeId,
+ IN OPTIONAL PCLIENT_ID ClientId
+ );
+
+NTSTATUS
+LsapSplitSid(
+ IN PSID AccountSid,
+ IN OUT PSID *DomainSid,
+ OUT ULONG *Rid
+ );
diff --git a/private/lsa/inc/ntlsa.bak b/private/lsa/inc/ntlsa.bak
new file mode 100644
index 000000000..32c2c39c1
--- /dev/null
+++ b/private/lsa/inc/ntlsa.bak
@@ -0,0 +1,2240 @@
+/*++ BUILD Version: 0007 // Increment this if a change has global effects
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ntlsa.h
+
+Abstract:
+
+ This module contains the public data structures and API definitions
+ needed to utilize Local Security Authority (LSA) services.
+
+
+Author:
+
+ Jim Kelly (JimK) 21-February-1991
+
+Revision History:
+
+--*/
+
+#ifndef _NTLSA_
+#define _NTLSA_
+
+// BUGBUG - ScottBi - Temporary definitions of new status codes
+
+#define STATUS_TOO_MANY_SECRETS ((NTSTATUS) 0xC0000500L)
+#define STATUS_SECRET_TOO_LONG ((NTSTATUS) 0xC0000501L)
+#define STATUS_INTERNAL_DB_ERROR ((NTSTATUS) 0xC0000502L)
+
+// BUGBUG - ScottBi - Operational Mode is obsolete, but is retained
+// until LsaRegisterLogonProgress is changed.
+
+//
+// Security operation mode of the system is held in a control
+// longword.
+//
+
+typedef ULONG LSA_OPERATIONAL_MODE, *PLSA_OPERATIONAL_MODE;
+
+//
+// The flags in the security operational mode are defined
+// as:
+//
+// PasswordProtected - Some level of authentication (such as
+// a password) must be provided by users before they are
+// allowed to use the system. Once set, this value will
+// not be cleared without re-booting the system.
+//
+// IndividualAccounts - Each user must identify an account to
+// logon to. This flag is only meaningful if the
+// PasswordProtected flag is also set. If this flag is
+// not set and the PasswordProtected flag is set, then all
+// users may logon to the same account. Once set, this value
+// will not be cleared without re-booting the system.
+//
+// MandatoryAccess - Indicates the system is running in a mandatory
+// access control mode (e.g., B-level as defined by the U.S.A's
+// Department of Defense's "Orange Book"). This is not utilized
+// in the current release of NT/OS2. This flag is only meaningful
+// if both the PasswordProtected and IndividualAccounts flags are
+// set. Once set, this value will not be cleared without
+// re-booting the system.
+//
+// LogFull - Indicates the system has been brought up in a mode in
+// which if must perform security auditing, but its audit log
+// is full. This may (should) restrict the operations that
+// can occur until the audit log is made not-full again. THIS
+// VALUE MAY BE CLEARED WHILE THE SYSTEM IS RUNNING (I.E., WITHOUT
+// REBOOTING).
+//
+// If the PasswordProtected flag is not set, then the system is running
+// without security, and user interface should be adjusted appropriately.
+//
+
+#define LSA_MODE_PASSWORD_PROTECTED (0x00000001L)
+#define LSA_MODE_INDIVIDUAL_ACCOUNTS (0x00000002L)
+#define LSA_MODE_MANDATORY_ACCESS (0x00000004L)
+#define LSA_MODE_LOG_FULL (0x00000008L)
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Widely used LSA data types //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// Defines for Count Limits on LSA API
+//
+
+#define LSA_MAXIMUM_SID_COUNT (0x00000100L)
+#define LSA_MAXIMUM_ENUMERATION_LENGTH (0x00001000L)
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Data types used by logon processes //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+//
+// Used by a logon process to indicate what type of logon is being
+// requested.
+//
+
+typedef enum _SECURITY_LOGON_TYPE {
+ Interactive = 2,
+ Network,
+ Batch
+} SECURITY_LOGON_TYPE, *PSECURITY_LOGON_TYPE;
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Data types related to Auditing //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// Audit Event Types
+//
+// The following are the built-in types of event that can be audited.
+//
+
+typedef enum _SECURITY_AUDIT_EVENT_TYPE {
+
+ SeAuditPrivilegedService,
+ SeAuditPrivilegedObject,
+ SeAuditAccessCheck,
+ SeAuditOpen,
+ SeAuditHandleAllocation,
+ SeAuditClose,
+ SeAuditTraverse,
+ SeAuditCreateInstance,
+ SeAuditImplicitAccess,
+ SeAuditObjectReference,
+ SeAuditCreateObject,
+ SeMaxAuditType
+
+} SECURITY_AUDIT_EVENT_TYPE, *PSECURITY_AUDIT_EVENT_TYPE;
+
+//
+// The following defines describe the auditing options for each
+// event type
+//
+
+// Leave options specified for this event unchanged
+
+#define SECURITY_AUDIT_EVENT_UNCHANGED (0x00000000L)
+
+// Audit successful occurrences of events of this type
+
+#define SECURITY_AUDIT_EVENT_SUCCESS (0x00000001L)
+
+// Audit failed attempts to cause an event of this type to occur
+
+#define SECURITY_AUDIT_EVENT_FAILURE (0x00000002L)
+
+#define SECURITY_AUDIT_EVENT_UNCHANGED (0x00000000L)
+
+#define SECURITY_AUDIT_EVENT_NONE (0x00000004L)
+
+// Mask of valid event auditing options
+
+#define SECURITY_AUDIT_EVENT_MASK \
+ (SECURITY_AUDIT_EVENT_SUCCESS | \
+ SECURITY_AUDIT_EVENT_FAILURE | \
+ SECURITY_AUDIT_EVENT_UNCHANGED | \
+ SECURITY_AUDIT_EVENT_NONE)
+
+//
+// Audit Event Options
+//
+
+typedef ULONG SECURITY_AUDIT_EVENT_OPTIONS, *PSECURITY_AUDIT_EVENT_OPTIONS;
+
+//
+// The following structures are used for passing audit information
+// between the kernel and LSA. Each structure corresponds to one type
+// of audit.
+//
+
+typedef struct _SECURITY_AUDIT_PRIVILEGED_SERVICE {
+ BOOLEAN AccessGranted;
+ PPRIVILEGE_SET PrivilegeSet;
+ PSTRING ServiceName;
+ PSTRING SubsystemName;
+ PSID UserSid;
+} SECURITY_AUDIT_PRIVILEGED_SERVICE, *PSECURITY_AUDIT_PRIVILEGED_SERVICE;
+
+/*
+ AccessGranted -
+
+ PrivilegeSet -
+
+ ServiceName -
+
+ SubsystemName -
+
+ UserSid -
+*/
+
+
+typedef struct _SECURITY_AUDIT_PRIVILEGED_OBJECT {
+ BOOLEAN AccessGranted;
+ ACCESS_MASK DesiredAccess;
+ PVOID HandleId;
+ PVOID Object; // OPTIONAL
+ PSTRING ObjectName; // OPTIONAL
+ PSTRING ObjectTypeName; // OPTIONAL
+ PPRIVILEGE_SET PrivilegeSet;
+ PSTRING SubsystemName; // OPTIONAL
+ PSID UserSid;
+} SECURITY_AUDIT_PRIVILEGED_OBJECT, *PSECURITY_AUDIT_PRIVILEGED_OBJECT;
+
+/*
+
+Description:
+
+ Used to hold audit information for calls to either
+ NtPrivilegeObjectAuditAlarm or SePrivilegedObjectAuditAlarm.
+
+ Since the former is called by subsystems and the latter by the
+ kernel, there will necessarily be some difference between the
+ information being logged in each case. Fields that are not common
+ between the two and may therefore may not be provided in one of
+ the cases are marked //OPTIONAL.
+
+ It may someday be advisable to create two structures rather than
+ use this one for both cases.
+
+Fields:
+
+ SubsystemName - Optionally provides the name of the subsystem
+ making the system service call.
+
+ PrivilegeSet - Provides the set of privileges to be logged.
+
+ HandleId - Provides the handle or (in the case of subsystem
+ maintained objects) an identifier to describe the object being
+ audited.
+
+ Object - Optionally provides the address of the object (kernel
+ mode objects only).
+
+ UserSid - Provides the Sid identifying the current user.
+
+ DesiredAccess - Provides the desired access to the object.
+
+ GrantedAccess - Provides the access actually granted to the
+ object.
+
+ AccessGranted - Provides whether or not access to the object was
+ permitted.
+
+ ObjectName - Optionally provides a string describing the name of
+ the object.
+
+ ObjectTypeName - Optionally provides a string describing the type
+ of the object.
+*/
+
+typedef struct _SECURITY_AUDIT_ACCESS_CHECK {
+ BOOLEAN AccessGranted;
+ ACCESS_MASK DesiredAccess;
+ ACCESS_MASK GrantedAccess;
+ PVOID HandleId;
+ BOOLEAN ObjectCreation;
+ PSTRING ObjectName;
+ PSTRING ObjectTypeName;
+ PPRIVILEGE_SET PrivilegeSet;
+ PSTRING SubsystemName;
+ PSID UserSid;
+} SECURITY_AUDIT_ACCESS_CHECK, *PSECURITY_AUDIT_ACCESS_CHECK;
+
+/*
+
+Description:
+
+ AccessGranted -
+
+ DesiredAccess -
+
+ GrantedAccess -
+
+ HandleId -
+
+ ObjectCreation -
+
+ ObjectName -
+
+ ObjectTypeName -
+
+ PrivilegeSet -
+
+ SubsystemName -
+
+ UserSid -
+
+*/
+
+
+typedef struct _SECURITY_AUDIT_OPEN {
+ BOOLEAN AccessGranted;
+ ACCESS_MASK DesiredAccess;
+ ACCESS_MASK GrantedAccess;
+ HANDLE HandleId; // OPTIONAL
+ PVOID Object; // OPTIONAL
+ BOOLEAN ObjectCreated;
+ PSTRING ObjectName; // OPTIONAL
+ PSTRING ObjectTypeName;
+ LUID OperationId;
+ PPRIVILEGE_SET PrivilegeSet; // OPTIONAL
+ PSTRING SubsystemName; // OPTIONAL
+ PSID UserSid;
+} SECURITY_AUDIT_OPEN, *PSECURITY_AUDIT_OPEN;
+
+/*
+
+Description:
+
+Fields:
+
+ AccessGranted -
+
+ DesiredAccess -
+
+ GrantedAccess -
+
+ HandleId -
+
+ Object -
+
+ ObjectCreated -
+
+ ObjectName -
+
+ ObjectTypeName -
+
+ OperationId -
+
+ PrivilegeSet -
+
+ SubsystemName -
+
+ UserSid -
+
+*/
+
+
+typedef struct _SECURITY_AUDIT_CLOSE {
+ PVOID HandleId;
+ PVOID Object;
+ PSTRING SubsystemName;
+ PSID UserSid;
+} SECURITY_AUDIT_CLOSE, *PSECURITY_AUDIT_CLOSE;
+
+
+typedef struct _SECURITY_AUDIT_TRAVERSE {
+ BOOLEAN AccessGranted;
+ PSTRING DirectoryName;
+ LUID OperationId;
+ PPRIVILEGE_SET PrivilegeSet;
+ PSID UserSid;
+} SECURITY_AUDIT_TRAVERSE, *PSECURITY_AUDIT_TRAVERSE;
+
+
+typedef struct _SECURITY_AUDIT_CREATE_OBJECT {
+ BOOLEAN AccessGranted;
+ ACCESS_MASK DesiredAccess;
+ PSTRING DirectoryName;
+ LUID OperationId;
+ PSID UserSid;
+} SECURITY_AUDIT_CREATE_OBJECT, *PSECURITY_AUDIT_CREATE_OBJECT;
+
+
+typedef struct _SECURITY_AUDIT_IMPLICIT_ACCESS {
+ BOOLEAN AccessGranted;
+ ACCESS_MASK DesiredAccess;
+ PSTRING ObjectTypeName;
+ LUID OperationId;
+ PPRIVILEGE_SET PrivilegeSet;
+ PSID UserSid;
+} SECURITY_AUDIT_IMPLICIT_ACCESS, *PSECURITY_AUDIT_IMPLICIT_ACCESS;
+
+
+typedef struct _SECURITY_AUDIT_HANDLE_ALLOCATION {
+ HANDLE HandleId;
+ LUID OperationId;
+} SECURITY_AUDIT_HANDLE_ALLOCATION, *PSECURITY_AUDIT_HANDLE_ALLOCATION;
+
+typedef struct _SECURITY_AUDIT_OBJECT_REFERENCE {
+ BOOLEAN AccessGranted;
+ ACCESS_MASK DesiredAccess;
+ PSTRING ObjectName;
+ PSTRING ObjectTypeName;
+ LUID OperationId;
+ PSID UserSid;
+} SECURITY_AUDIT_OBJECT_REFERENCE, *PSECURITY_AUDIT_OBJECT_REFERENCE;
+
+
+
+typedef struct _SECURITY_AUDIT_CREATE_INSTANCE {
+ BOOLEAN AccessGranted;
+ PSTRING ObjectTypeName;
+ LUID OperationId;
+ PSID UserSid;
+} SECURITY_AUDIT_CREATE_INSTANCE, *PSECURITY_AUDIT_CREATE_INSTANCE;
+
+//
+// Audit Record
+//
+
+typedef struct _SECURITY_AUDIT_RECORD {
+
+ LIST_ENTRY Links;
+ ULONG AuditRecordLength;
+ SECURITY_AUDIT_EVENT_TYPE AuditEventType;
+ ULONG AuditInformationOffset;
+
+} SECURITY_AUDIT_RECORD, *PSECURITY_AUDIT_RECORD;
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Services provided for use by general users //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+NTSTATUS
+LsaLookupPrivilegeValue(
+ IN PSTRING Name,
+ OUT PLUID Value
+ );
+
+NTSTATUS
+LsaLookupPrivilegeName(
+ IN PLUID Value,
+ OUT PSTRING Name
+ );
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Services provided for use by logon processes //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsaRegisterLogonProcess (
+ IN PSTRING LogonProcessName,
+ OUT PHANDLE LsaHandle,
+ OUT PLSA_OPERATIONAL_MODE SecurityMode
+ );
+
+NTSTATUS
+LsaLookupAuthenticationPackage (
+ IN HANDLE LsaHandle,
+ IN PSTRING PackageName,
+ OUT PULONG AuthenticationPackage
+ );
+
+NTSTATUS
+LsaFreeReturnBuffer (
+ IN PVOID Buffer
+ );
+
+
+NTSTATUS
+LsaLogonUser (
+ IN HANDLE LsaHandle,
+ IN PSTRING OriginName,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG AuthenticationPackage,
+ IN PVOID AuthenticationInformation,
+ IN ULONG AuthenticationInformationLength,
+ IN PTOKEN_GROUPS LocalGroups OPTIONAL,
+ IN PTOKEN_SOURCE SourceContext,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferLength,
+ OUT PGUID LogonId,
+ OUT PHANDLE Token,
+ OUT PNTSTATUS SubStatus
+ );
+
+NTSTATUS
+LsaCallAuthenticationPackage (
+ IN HANDLE LsaHandle,
+ IN ULONG AuthenticationPackage,
+ IN PVOID ProtocolSubmitBuffer,
+ IN ULONG SubmitBufferLength,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferLength,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Data types used by authentication packages //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// opaque data type which represents a client request
+//
+
+typedef PVOID *PLSA_CLIENT_REQUEST;
+
+
+//
+// When a logon of a user is requested, the authentication package
+// is expected to return one of the following structures indicating
+// the contents of a user's token.
+//
+
+typedef enum _LSA_TOKEN_INFORMATION_TYPE {
+ LsaTokenInformationNull, // Implies LSA_TOKEN_INFORMATION_NULL data type
+ LsaTokenInformationV1 // Implies LSA_TOKEN_INFORMATION_V1 data type
+} LSA_TOKEN_INFORMATION_TYPE, *PLSA_TOKEN_INFORMATION_TYPE;
+
+
+//
+// The NULL information is used in cases where a non-authenticated
+// system access is needed. For example, a non-authentication network
+// circuit (such as LAN Manager's null session) can be given NULL
+// information. This will result in an anonymous token being generated
+// for the logon that gives the user no ability to access protected system
+// resources, but does allow access to non-protected system resources.
+//
+
+typedef struct _LSA_TOKEN_INFORMATION_NULL {
+
+ //
+ // Time at which the security context becomes invalid.
+ // Use a value in the distant future if the context
+ // never expires.
+ //
+
+ LARGE_INTEGER ExpirationTime;
+
+ //
+ // The SID(s) of groups the user is to be made a member of. This should
+ // not include WORLD or other system defined and assigned
+ // SIDs. These will be added automatically by LSA.
+ //
+ // Each SID is expected to be in a separately allocated block
+ // of memory. The TOKEN_GROUPS structure is also expected to
+ // be in a separately allocated block of memory.
+ //
+
+ PTOKEN_GROUPS Groups;
+
+} LSA_TOKEN_INFORMATION_NULL, *PLSA_TOKEN_INFORMATION_NULL;
+
+
+//
+// The V1 information is used in most cases of logon. This structure
+// contains information that an authentication package can place in a
+// Version 1 NT/OS2 token object.
+//
+
+typedef struct _LSA_TOKEN_INFORMATION_V1 {
+
+ //
+ // Time at which the security context becomes invalid.
+ // Use a value in the distant future if the context
+ // never expires.
+ //
+
+ LARGE_INTEGER ExpirationTime;
+
+ //
+ // The SID of the user logging on. The SID value is in a
+ // separately allocated block of memory.
+ //
+
+ TOKEN_USER User;
+
+ //
+ // The SID(s) of groups the user is a member of. This should
+ // not include WORLD or other system defined and assigned
+ // SIDs. These will be added automatically by LSA.
+ //
+ // Each SID is expected to be in a separately allocated block
+ // of memory. The TOKEN_GROUPS structure is also expected to
+ // be in a separately allocated block of memory.
+ //
+
+ PTOKEN_GROUPS Groups;
+
+ //
+ // This field is used to establish the primary group of the user.
+ // This value does not have to correspond to one of the SIDs
+ // assigned to the user.
+ //
+ // The SID pointed to by this structure is expected to be in
+ // a separately allocated block of memory.
+ //
+ // This field is mandatory and must be filled in.
+ //
+
+ TOKEN_PRIMARY_GROUP PrimaryGroup;
+
+
+
+ //
+ // The privileges the user is assigned. This list of privileges
+ // will be augmented or over-ridden by any local security policy
+ // assigned privileges.
+ //
+ // Each privilege is expected to be in a separately allocated
+ // block of memory. The TOKEN_PRIVILEGES structure is also
+ // expected to be in a separately allocated block of memory.
+ //
+ // If there are no privileges to assign to the user, this field
+ // may be set to NULL.
+ //
+
+ PTOKEN_PRIVILEGES Privileges;
+
+
+
+ //
+ // This field may be used to establish an explicit default
+ // owner. Normally, the user ID is used as the default owner.
+ // If another value is desired, it must be specified here.
+ //
+ // The Owner.Sid field may be set to NULL to indicate there is no
+ // alternate default owner value.
+ //
+
+ TOKEN_OWNER Owner;
+
+ //
+ // This field may be used to establish a default
+ // protection for the user. If no value is provided, then
+ // a default protection that grants everyone all access will
+ // be established.
+ //
+ // The DefaultDacl.DefaultDacl field may be set to NULL to indicate
+ // there is no default protection.
+ //
+
+ TOKEN_DEFAULT_DACL DefaultDacl;
+
+} LSA_TOKEN_INFORMATION_V1, *PLSA_TOKEN_INFORMATION_V1;
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Interface definitions available for use by authentication packages //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+typedef NTSTATUS
+(*PLSA_CREATE_LOGON_SESSION) (
+ IN PGUID LogonId
+ );
+
+typedef NTSTATUS
+(*PLSA_DELETE_LOGON_SESSION) (
+ IN PGUID LogonId
+ );
+
+typedef NTSTATUS
+(*PLSA_ADD_CREDENTIAL) (
+ IN PGUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN PSTRING PrimaryKeyValue,
+ IN PSTRING Credentials
+ );
+
+typedef NTSTATUS
+(*PLSA_GET_CREDENTIALS) (
+ IN PGUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN OUT PULONG QueryContext,
+ IN BOOLEAN RetrieveAllCredentials,
+ IN PSTRING PrimaryKeyValue,
+ OUT PULONG PrimaryKeyLength,
+ IN PSTRING Credentials
+ );
+
+typedef NTSTATUS
+(*PLSA_DELETE_CREDENTIAL) (
+ IN PGUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN PSTRING PrimaryKeyValue
+ );
+
+typedef PVOID
+(*PLSA_ALLOCATE_LSA_HEAP) (
+ IN ULONG Length
+ );
+
+typedef VOID
+(*PLSA_FREE_LSA_HEAP) (
+ IN PVOID Base
+ );
+
+typedef NTSTATUS
+(*PLSA_ALLOCATE_CLIENT_BUFFER) (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG LengthRequired,
+ OUT PVOID *ClientBaseAddress
+ );
+
+typedef NTSTATUS
+(*PLSA_FREE_CLIENT_BUFFER) (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ClientBaseAddress
+ );
+
+typedef NTSTATUS
+(*PLSA_COPY_TO_CLIENT_BUFFER) (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG Length,
+ IN PVOID ClientBaseAddress,
+ IN PVOID BufferToCopy
+ );
+
+typedef NTSTATUS
+(*PLSA_COPY_FROM_CLIENT_BUFFER) (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG Length,
+ IN PVOID BufferToCopy,
+ IN PVOID ClientBaseAddress
+ );
+
+
+
+//
+// The dispatch table of LSA services which are available to
+// authentication packages.
+//
+typedef struct _LSA_DISPATCH_TABLE {
+ PLSA_CREATE_LOGON_SESSION CreateLogonSession;
+ PLSA_DELETE_LOGON_SESSION DeleteLogonSession;
+ PLSA_ADD_CREDENTIAL AddCredential;
+ PLSA_GET_CREDENTIALS GetCredentials;
+ PLSA_DELETE_CREDENTIAL DeleteCredential;
+ PLSA_ALLOCATE_LSA_HEAP AllocateLsaHeap;
+ PLSA_FREE_LSA_HEAP FreeLsaHeap;
+ PLSA_ALLOCATE_CLIENT_BUFFER AllocateClientBuffer;
+ PLSA_FREE_CLIENT_BUFFER FreeClientBuffer;
+ PLSA_COPY_TO_CLIENT_BUFFER CopyToClientBuffer;
+ PLSA_COPY_FROM_CLIENT_BUFFER CopyFromClientBuffer;
+} LSA_DISPATCH_TABLE, *PLSA_DISPATCH_TABLE;
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Interface definitions of services provided by authentication packages //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+
+//
+// Routine names
+//
+// The routines provided by the DLL must be assigned the following names
+// so that their addresses can be retrieved when the DLL is loaded.
+//
+
+#define LSA_AP_NAME_INITIALIZE_PACKAGE "LsaApInitializePackage\0"
+#define LSA_AP_NAME_LOGON_USER "LsaApLogonUser\0"
+#define LSA_AP_NAME_CALL_PACKAGE "LsaApCallPackage\0"
+#define LSA_AP_NAME_LOGON_TERMINATED "LsaApLogonTerminated\0"
+
+
+//
+// Routine templates
+//
+
+
+typedef NTSTATUS
+(*PLSA_AP_INITIALIZE_PACKAGE) (
+ IN ULONG AuthenticationPackageId,
+ IN PLSA_DISPATCH_TABLE LsaDispatchTable,
+ IN PSTRING Database OPTIONAL,
+ IN PSTRING Confidentiality OPTIONAL,
+ OUT PSTRING *AuthenticationPackageName
+ );
+
+typedef NTSTATUS
+(*PLSA_AP_LOGON_USER) (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PVOID AuthenticationInformation,
+ IN PVOID ClientAuthenticationBase,
+ IN ULONG AuthenticationInformationLength,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferLength,
+ OUT PGUID LogonId,
+ OUT PNTSTATUS SubStatus,
+ OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ OUT PVOID *TokenInformation
+ );
+
+typedef NTSTATUS
+(*PLSA_AP_CALL_PACKAGE) (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferLength,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferLength,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+typedef VOID
+(*PLSA_AP_LOGON_TERMINATED) (
+ IN PGUID LogonId
+ );
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy Administration API datatypes and defines //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+//
+// Access types for the Policy object
+//
+
+#define POLICY_VIEW_LOCAL_INFORMATION 0x00000001L
+#define POLICY_VIEW_AUDIT_INFORMATION 0x00000002L
+#define POLICY_GET_PRIVATE_INFORMATION 0x00000004L
+#define POLICY_TRUST_ADMIN 0x00000008L
+#define POLICY_CREATE_ACCOUNT 0x00000010L
+#define POLICY_CREATE_SECRET 0x00000020L
+#define POLICY_CREATE_PRIVILEGE 0x00000040L
+#define POLICY_SET_DEFAULT_QUOTA_LIMITS 0x00000080L
+#define POLICY_SET_AUDIT_REQUIREMENTS 0x00000100L
+#define POLICY_AUDIT_LOG_ADMIN 0x00000200L
+#define POLICY_SERVER_ADMIN 0x00000800L
+#define POLICY_LOOKUP_NAMES 0x00001000L
+
+#define POLICY_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ POLICY_VIEW_LOCAL_INFORMATION | \
+ POLICY_VIEW_AUDIT_INFORMATION | \
+ POLICY_GET_PRIVATE_INFORMATION | \
+ POLICY_TRUST_ADMIN | \
+ POLICY_CREATE_ACCOUNT | \
+ POLICY_CREATE_SECRET | \
+ POLICY_CREATE_PRIVILEGE | \
+ POLICY_SET_DEFAULT_QUOTA_LIMITS | \
+ POLICY_SET_AUDIT_REQUIREMENTS | \
+ POLICY_AUDIT_LOG_ADMIN | \
+ POLICY_CREATE_PRIVILEGE | \
+ POLICY_SERVER_ADMIN | \
+ POLICY_LOOKUP_NAMES )
+
+
+//
+// Policy object specific data types.
+//
+
+//
+// The following data type is used to identify a domain
+//
+
+typedef struct _LSA_TRUST_INFORMATION {
+
+ UNICODE_STRING Name;
+ PSID Sid;
+
+} LSA_TRUST_INFORMATION, *PLSA_TRUST_INFORMATION;
+
+// where members have the following usage:
+//
+// Name - The name of the domain.
+//
+// Sid - A pointer to the Sid of the Domain
+//
+
+//
+// The following data type is used in name and SID lookup services to
+// describe the domains referenced in the lookup operation.
+//
+
+typedef struct _LSA_REFERENCED_DOMAIN_LIST {
+
+ ULONG Entries;
+ PLSA_TRUST_INFORMATION Domains;
+
+} LSA_REFERENCED_DOMAIN_LIST, *PLSA_REFERENCED_DOMAIN_LIST;
+
+// where members have the following usage:
+//
+// Entries - Is a count of the number of domains described in the
+// Domains array.
+//
+// Domains - Is a pointer to an array of Entries LSA_TRUST_INFORMATION data
+// structures.
+//
+
+
+//
+// The following data type is used in name to SID lookup services to describe
+// the domains referenced in the lookup operation.
+//
+
+typedef struct _LSA_TRANSLATED_SID {
+
+ SID_NAME_USE Use;
+ ULONG RelativeId;
+ LONG DomainIndex;
+
+} LSA_TRANSLATED_SID, *PLSA_TRANSLATED_SID;
+
+// where members have the following usage:
+//
+// Use - identifies the use of the SID. If this value is SidUnknown or
+// SidInvalid, then the remainder of the record is not set and
+// should be ignored.
+//
+// RelativeId - Contains the relative ID of the translated SID. The
+// remainder of the SID (the prefix) is obtained using the
+// DomainIndex field.
+//
+// DomainIndex - Is the index of an entry in a related
+// LSA_REFERENCED_DOMAIN_LIST data structure describing the
+// domain in which the account was found.
+//
+// If there is no corresponding reference domain for an entry, then
+// this field will contain a negative value.
+//
+
+
+//
+// The following data type is used in SID to name lookup services to
+// describe the domains referenced in the lookup operation.
+//
+
+typedef struct _LSA_TRANSLATED_NAME {
+
+ SID_NAME_USE Use;
+ UNICODE_STRING Name;
+ LONG DomainIndex;
+
+} LSA_TRANSLATED_NAME, *PLSA_TRANSLATED_NAME;
+
+// where the members have the following usage:
+//
+// Use - Identifies the use of the name. If this value is SidUnknown
+// or SidInvalid, then the remainder of the record is not set and
+// should be ignored. If this value is SidWellKnownGroup then the
+// Name field is invalid, but the DomainIndex field is not.
+//
+// Name - Contains the isolated name of the translated SID.
+//
+// DomainIndex - Is the index of an entry in a related
+// LSA_REFERENCED_DOMAIN_LIST data structure describing the domain
+// in which the account was found.
+//
+// If there is no corresponding reference domain for an entry, then
+// this field will contain a negative value.
+//
+
+//
+// The following data type specifies the ways in whcih a user or member of
+// an alias or group may be allowed to access the system. An account may
+// be granted zero or more of these types of access to the system.
+//
+// The types of access are:
+//
+// Interactive - The user or alias/group member may interactively logon
+// to the system.
+//
+// Network - The user or alias/group member may access the system via
+// the network (e.g., through shares).
+//
+// Service - The user or alias may be activated as a service on the
+// system.
+//
+
+typedef ULONG POLICY_SYSTEM_ACCESS_MODE, *PPOLICY_SYSTEM_ACCESS_MODE;
+
+#define POLICY_MODE_INTERACTIVE (0x00000001L)
+#define POLICY_MODE_NETWORK (0x00000002L)
+#define POLICY_MODE_SERVICE (0x00000004L)
+
+//
+// The following data type is used to represent the role of the LSA
+// server (primary or backup).
+//
+
+typedef enum _POLICY_LSA_SERVER_ROLE {
+
+ PolicyServerRoleBackup = 2,
+ PolicyServerRolePrimary
+
+} POLICY_LSA_SERVER_ROLE, *PPOLICY_LSA_SERVER_ROLE;
+
+
+//
+// The following data type is used to represent the state of the LSA
+// server (enabled or disabled). Some operations may only be performed on
+// an enabled LSA server.
+//
+
+typedef enum _POLICY_SERVER_ENABLE_STATE {
+
+ PolicyServerEnabledBackup = 2,
+ PolicyServerEnabledPrimary
+
+} POLICY_SERVER_ENABLE_STATE, *PPOLICY_SERVER_ENABLE_STATE;
+
+
+//
+// The following data type is used to specify the auditing options for
+// an Audit Event Type.
+//
+
+typedef ULONG POLICY_AUDIT_EVENT_OPTIONS, *PPOLICY_AUDIT_EVENT_OPTIONS;
+
+// where the following flags can be set:
+//
+// POLICY_AUDIT_EVENT_UNCHANGED - Leave existing auditing options
+// unchanged for events of this type. This flag is only used for
+// set operations. If this flag is set, then all other flags
+// are ignored.
+//
+// POLICY_AUDIT_EVENT_NONE - Cancel all auditing options for events
+// of this type. If this flag is set, the success/failure flags
+// are ignored.
+//
+// POLICY_AUDIT_EVENT_SUCCESS - When auditing is enabled, audit all
+// successful occurrences of events of the given type.
+//
+// POLICY_AUDIT_EVENT_FAILURE - When auditing is enabled, audit all
+// unsuccessful occurrences of events of the given type.
+//
+
+
+//
+// The following data type is used to return information about privileges
+// defined on a system.
+//
+
+typedef struct _POLICY_PRIVILEGE_DEFINITION {
+
+ UNICODE_STRING Name;
+ LUID LocalValue;
+
+} POLICY_PRIVILEGE_DEFINITION, *PPOLICY_PRIVILEGE_DEFINITION;
+
+// where the members have the following usage:
+//
+// Name - Is the architected name of the privilege. This is the
+// primary key of the privilege and the only value that is
+// transportable between systems.
+//
+// Luid - is a LUID value assigned locally for efficient representation
+// of the privilege. Ths value is meaningful only on the system it
+// was assigned on and is not transportable in any way.
+//
+
+
+//
+// The following data type defines the classes of Policy Information
+// that may be queried/set.
+//
+
+typedef enum _POLICY_INFORMATION_CLASS {
+
+ PolicyAuditLogInformation = 1,
+ PolicyAuditEventsInformation,
+ PolicyPrimaryDomainInformation,
+ PolicyPdAccountInformation,
+ PolicyAccountDomainInformation,
+ PolicyLsaServerRoleInformation,
+ PolicyReplicaSourceInformation,
+ PolicyDefaultQuotaInformation,
+ PolicyModificationInformation
+
+} POLICY_INFORMATION_CLASS, *PPOLICY_INFORMATION_CLASS;
+
+
+//
+// The following data type corresponds to the PolicyAuditLogInformation
+// information class. It is used to represent information relating to
+// the Audit Log.
+//
+// This structure may be used in both query and set operations. However,
+// when used in set operations, some fields are ignored.
+//
+
+typedef struct _POLICY_AUDIT_LOG_INFO {
+
+ ULONG AuditLogPercentFull;
+ ULONG MaximumLogSize;
+ TIME AuditRetentionPeriod;
+ BOOLEAN AuditLogFullShutdownInProgress;
+ TIME TimeToShutdown;
+
+} POLICY_AUDIT_LOG_INFO, *PPOLICY_AUDIT_LOG_INFO;
+
+// where the members have the following usage:
+//
+// AuditLogPercentFull - Indicates the percentage of the Audit Log
+// currently being used.
+//
+// MaximumLogSize - Specifies the maximum size of the Audit Log in
+// kilobytes.
+//
+// AuditRetentionPeriod - Indicates the length of time that Audit
+// Records are to be retained. Audit Records are discardable
+// if their timestamp predates the current time minus the
+// retention period.
+//
+// AuditLogFullShutdownInProgress - Indicates whether or not a system
+// shutdown is being initiated due to the security Audit Log becoming
+// full. This condition will only occur if the system is configured
+// to shutdown when the log becomes full.
+//
+// TRUE indicates that a shutdown is in progress
+// FALSE indicates that a shutdown is not in progress.
+//
+// Once a shutdown has been initiated, this flag will be set to
+// TRUE. If an administrator is able to currect the situation
+// before the shutdown becomes irreversible, then this flag will
+// be reset to false.
+//
+// This field is ignored for set operations.
+//
+// TimeToShutdown - If the AuditLogFullShutdownInProgress flag is set,
+// then this field contains the time left before the shutdown
+// becomes irreversible.
+//
+// This field is ignored for set operations.
+//
+
+
+//
+// The following data type corresponds to the PolicyAuditEventsInformation
+// information class. It is used to represent information relating to
+// the audit requirements.
+//
+
+typedef struct _POLICY_AUDIT_EVENTS_INFO {
+
+ BOOLEAN AuditingMode;
+ PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions;
+ ULONG MaximumAuditEventCount;
+
+} POLICY_AUDIT_EVENTS_INFO, *PPOLICY_AUDIT_EVENTS_INFO;
+
+// where the members have the following usage:
+//
+// AuditingMode - A Boolean variable specifying the Auditing Mode value.
+// This value is interpreted as follows:
+//
+// TRUE - Auditing is to be enabled (set operations) or is enabled
+// (query operations). Audit Records will be generated according
+// to the Event Auditing Options in effect (see the
+// EventAuditingOptions field.
+//
+// FALSE - Auditing is to be disabled (set operations) or is
+// disabled (query operations). No Audit Records will be
+// generated. Note that for set operations the Event Auditing
+// Options in effect will still be updated as specified by the
+// EventAuditingOptions field whether Auditing is enabled or
+// disabled.
+//
+// EventAuditingOptions - Pointer to an array of Auditing Options
+// indexed by Audit Event Type.
+//
+// MaximumAuditEventCount - Specifiesa count of the number of Audit
+// Event Types specified by the EventAuditingOptions parameter. If
+// this count is less than the number of Audit Event Types supported
+// by the system, the Auditing Options for Event Types with IDs
+// higher than (MaximumAuditEventCount + 1) are left unchanged.
+//
+
+
+//
+// The following structure corresponds to the PolicyAccountDomainInformation
+// information class.
+//
+
+typedef struct _POLICY_ACCOUNT_DOMAIN_INFO {
+
+ PUNICODE_STRING DomainName;
+ PSID DomainSid;
+
+} POLICY_ACCOUNT_DOMAIN_INFO, *PPOLICY_ACCOUNT_DOMAIN_INFO;
+
+// where the members have the following usage:
+//
+// DomainName - Is the name of the domain
+//
+// DomainSid - Is the Sid of the domain
+//
+
+
+//
+// The following structure corresponds to the PolicyPrimaryDomainInformation
+// information class.
+//
+
+typedef struct _POLICY_PRIMARY_DOMAIN_INFO {
+
+ PUNICODE_STRING Name;
+ PSID Sid;
+
+} POLICY_PRIMARY_DOMAIN_INFO, *PPOLICY_PRIMARY_DOMAIN_INFO;
+
+// where the members have the following usage:
+//
+// Name - Is the name of the domain
+//
+// Sid - Is the Sid of the domain
+//
+
+
+//
+// The following structure corresponds to the PolicyPdAccountInformation
+// information class. This structure may be used in Query operations
+// only.
+//
+
+typedef struct _POLICY_PD_ACCOUNT_INFO {
+
+ UNICODE_STRING Name;
+
+} POLICY_PD_ACCOUNT_INFO, *PPOLICY_PD_ACCOUNT_INFO;
+
+// where the members have the following usage:
+//
+// Name - Is the name of an account in the domain that should be used
+// for authentication and name/ID lookup requests.
+//
+
+
+//
+// The following structure corresponds to the PolicyLsaServerRoleInformation
+// information class.
+//
+
+typedef struct _POLICY_LSA_SERVER_ROLE_INFO {
+
+ POLICY_LSA_SERVER_ROLE LsaServerRole;
+
+} POLICY_LSA_SERVER_ROLE_INFO, *PPOLICY_LSA_SERVER_ROLE_INFO;
+
+// where the fields have the following usage:
+//
+// TBS
+//
+
+
+//
+// The following structure corresponds to the PolicyReplicaSourceInformation
+// information class.
+//
+
+typedef struct _POLICY_REPLICA_SOURCE_INFO {
+
+ PUNICODE_STRING ReplicaSource;
+ PUNICODE_STRING ReplicaAccountName;
+
+} POLICY_REPLICA_SOURCE_INFO, *PPOLICY_REPLICA_SOURCE_INFO;
+
+
+//
+// The following structure corresponds to the PolicyDefaultQuotaInformation
+// information class.
+//
+
+typedef struct _POLICY_DEFAULT_QUOTA_INFO {
+
+ QUOTA_LIMITS QuotaLimits;
+
+} POLICY_DEFAULT_QUOTA_INFO, *PPOLICY_DEFAULT_QUOTA_INFO;
+
+
+//
+// The following structure corresponds to the PolicyModificationInformation
+// information class.
+//
+
+typedef struct _POLICY_MODIFICATION_INFO {
+
+ LARGE_INTEGER ModifiedId;
+ LARGE_INTEGER DatabaseCreationTime;
+
+} POLICY_MODIFICATION_INFO, *PPOLICY_MODIFICATION_INFO;
+
+// where the members have the following usage:
+//
+// ModifiedId - Is a 64-bit unsigned integer that is incremented each
+// time anything in the LSA database is modified. This value is
+// only modified on Primary Domain Controllers.
+//
+// DatabaseCreationTime - Is the date/time that the LSA Database was
+// created. On Backup Domain Controllers, this value is replicated
+// from the Primary Domain Controller.
+//
+
+
+//
+// Account object type-specific Access Types
+//
+
+#define LSA_ACCOUNT_VIEW 0x00000001L
+#define LSA_ACCOUNT_ADJUST_PRIVILEGES 0x00000002L
+#define LSA_ACCOUNT_ADJUST_QUOTAS 0x00000004L
+#define LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS 0x00000008L
+
+#define LSA_ACCOUNT_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ LSA_ACCOUNT_VIEW | \
+ LSA_ACCOUNT_ADJUST_PRIVILEGES | \
+ LSA_ACCOUNT_ADJUST_QUOTAS | \
+ LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS )
+
+//
+// LSA RPC Context Handle (Opaque form). Note that a Context Handle is
+// always a pointer type unlike regular handles.
+//
+
+typedef PVOID LSA_HANDLE, *PLSA_HANDLE;
+
+//
+// Trusted Domain object specific access types
+//
+
+#define TRUSTED_QUERY_ACCOUNT_NAME 0x00000001L
+#define TRUSTED_QUERY_CONTROLLERS 0x00000002L
+#define TRUSTED_SET_CONTROLLERS 0x00000004L
+
+#define TRUSTED_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ TRUSTED_QUERY_ACCOUNT_NAME | \
+ TRUSTED_QUERY_CONTROLLERS | \
+ TRUSTED_SET_CONTROLLERS )
+
+//
+// Trusted Domain Object specific data types
+//
+
+//
+// This data type defines the following information classes that may be
+// queried or set.
+//
+
+typedef enum _TRUSTED_INFORMATION_CLASS {
+
+ TrustedAccountNameInformation = 1,
+ TrustedControllersInformation,
+ TrustedPosixOffsetInformation
+
+} TRUSTED_INFORMATION_CLASS, *PTRUSTED_INFORMATION_CLASS;
+
+//
+// The following data type corresponds to the TrustedAccountNameInformation
+// information class.
+//
+
+typedef struct _TRUSTED_ACCOUNT_NAME_INFO {
+
+ UNICODE_STRING Name;
+
+} TRUSTED_ACCOUNT_NAME_INFO, *PTRUSTED_ACCOUNT_NAME_INFO;
+
+// where members have the following meaning:
+//
+// Name - The name of an account in the domain that should be used for
+// authentication and name/id lookup requests.
+//
+
+
+//
+// The following data type corresponds to the TrustedControllersInformation
+// information class.
+//
+
+typedef struct _TRUSTED_CONTROLLERS_INFO {
+
+ ULONG Entries;
+ PUNICODE_STRING Names;
+
+} TRUSTED_CONTROLLERS_INFO, *PTRUSTED_CONTROLLERS_INFO;
+
+// where members have the following meaning:
+//
+// Entries - Indicate how mamy entries there are in the Names array.
+//
+// Names - Pointer to an array of UNICODE_STRING structures containing the
+// names of domain controllers of the domain. This information may not
+// be accurate and should be used only as a hint. The order of this
+// list is considered significant and will be maintained.
+//
+// By convention, the first name in this list is assumed to be the
+// Primary Domain Controller of the domain. If the Primary Domain
+// Controller is not known, the first name should be set to the NULL
+// string.
+//
+
+
+//
+// The following data type corresponds to the TrustedPosixOffsetInformation
+// information class.
+//
+
+typedef struct _TRUSTED_POSIX_OFFSET_INFO {
+
+ ULONG Offset;
+
+} TRUSTED_POSIX_OFFSET_INFO, *PTRUSTED_POSIX_OFFSET_INFO;
+
+// where members have the following meaning:
+//
+// Offset - Is an offset to use for the generation of Posix user and group
+// IDs from SIDs. The Posix ID corresponding to any particular SID is
+// generated by adding the RID of that SID to the Offset of the SID's
+// corresponding TrustedDomain object.
+//
+
+
+//
+// Secret object specific access types
+//
+
+#define SECRET_SET_VALUE 0x00000001L
+#define SECRET_QUERY_VALUE 0x00000002L
+
+#define SECRET_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ SECRET_SET_VALUE | \
+ SECRET_QUERY_VALUE )
+
+//
+// Secret object specific data types.
+//
+
+//
+// Secret object limits
+//
+
+#define LSA_SECRET_MAXIMUM_COUNT 0x00000100L
+#define LSA_SECRET_MAXIMUM_LENGTH 0x00000200L
+
+//
+// LSA Enumeration Context
+//
+
+typedef ULONG LSA_ENUMERATION_HANDLE, *PLSA_ENUMERATION_HANDLE;
+
+//
+// LSA Enumeration Information
+//
+
+typedef struct _LSA_ENUMERATION_INFORMATION {
+
+ PSID Sid;
+
+} LSA_ENUMERATION_INFORMATION, *PLSA_ENUMERATION_INFORMATION;
+
+//
+// Audit Log Information
+//
+// This data type is used to return the audit log information held by LSA. This
+// information comprises the following fields:
+//
+// AuditLogPercentFull - Indicates what percentage of the audit
+// log quota is currently being used.
+//
+// MaximumLogSize - Specifies the maximum size in Kilobytes that the
+// Audit Log will be allowed to grow to. If the audit log already
+// exceeds this size, this parameter takes no effect and no
+// truncation of the existing log occurs.
+//
+// AuditRetentionPeriod - Indicates the length of time security
+// audit information is held before being discarded.
+//
+// AuditLogFullShutdownInProcess - Indicates whether or not a
+// system shutdown is being initiated due to the security
+// audit log becoming full. This condition will only occur
+// if the system is configured to shutdown when the audit
+// log is full. TRUE indicats a shutdown is in progress.
+// FALSE indicates a shutdown is not in progress.
+//
+// Once a shutdown has been initiated, this flag will be set
+// to TRUE. If an administrator is able to correct the
+// situation before the shutdown becomes irreversible, then
+// this flag will be reset to FALSE.
+//
+// TimeToShutdown - If the AuditLogFullShutdownInProcess flag is
+// set, then this field contains the time left before the
+// shutdown becomes irreversible.
+//
+
+typedef struct _LSA_AUDIT_LOG_INFORMATION {
+
+ ULONG AuditLogPercentFull;
+ ULONG MaximumLogSize;
+ ULONG NextAuditRecordId;
+ LARGE_INTEGER AuditRetentionPeriod;
+ BOOLEAN AuditLogFullShutdownInProgress;
+ LARGE_INTEGER TimeToShutdown;
+
+} LSA_AUDIT_LOG_INFORMATION, *PLSA_AUDIT_LOG_INFORMATION;
+
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Miscellaneous API function prototypes //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsaFreeMemory(
+ IN PVOID Buffer
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaFreeMemory API frees memory allocated by an Lsa API for an output
+ buffer. All LSA API that return variable length output buffers always
+ allocate the buffer on behalf of the client. The client is responsible
+ for freeing this memory by passing the returned buffer pointer to this
+ API when the memory is no longer required.
+
+Arguments:
+
+ Buffer - Pointer to memory buffer that was allocated by an LSA API call.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+
+NTSTATUS
+LsaClose(
+ IN LSA_HANDLE ObjectHandle
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaClose API closes a handle to an open object within the database.
+ If closing a handle to the Policy object and there are no objects still
+ open within the current connection to the LSA, the connection is closed.
+ If a handle to an object within the database is closed and the object is
+ marked for DELETE access, the object will be deleted when the last handle
+ to that object is closed.
+
+Arguments:
+
+ ObjectHandle - Handle returned from an LsaOpen<object type> or
+ LsaCreate<object type> call.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+
+NTSTATUS
+LsaDelete(
+ IN LSA_HANDLE ObjectHandle
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaDelete API causes the corresponding object to be deleted and
+ the handle to be closed.
+
+Arguments:
+
+ ObjectHandle - Specifies the object to be deleted. The object must be
+ open for DELETE access. Following successful completion of this call,
+ the handle value is no longer valid.
+
+Return Values:
+
+None specific to this API.
+
+--*/
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Policy Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsaOpenPolicy(
+ IN PUNICODE_STRING SystemName OPTIONAL,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN OUT PLSA_HANDLE PolicyHandle
+ );
+
+/*++
+
+Routine Description:
+
+ To administer the Local Security Policy of a local or remote system,
+ this API must be called to establish a session with that system's
+ Local Security Authority (LSA) subsystem. This API connects to
+ the LSA of the target system and opens the Policy object
+ of the target system's Local Security Policy database. A handle to
+ the Policy object is returned. This handle must be used
+ on all subsequent API calls to administer the Local Security Policy
+ information for the target system.
+
+Arguments:
+
+ ServerName - Name of the system to be administered. Administration of
+ the local system is assumed if NULL is specified.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the LSA Subsystem's LSA Database. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target Policy object to determine whether the accesses will be granted or denied.
+
+ ObjectAttributes - Pointer to the set of attributes to use for this
+ connection. The security Quality Of Service information is used and
+ normally should provide Security Identification level of
+ impersonation. Some operations, however, require Security
+ Impersonation level of impersonation.
+
+ PolicyHandle - Receives a handle to be used in future requests.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have access to the target
+ system's LSA Database, or does not have other desired accesses.
+--*/
+
+
+NTSTATUS
+LsaQueryInformationPolicy(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG InformationLevel,
+ OUT PVOID *Buffer
+ );
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsarQueryInformationPolicy API.
+
+ The LsaQueryInformationPolicy API obtains information from the Policy
+ object. The caller must have access appropriate to the information
+ being requested (see Informationlevel parameter).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationLevel - Specifies the information to be returned. The
+ information levels and accesses required are as follows:
+
+ Information Level Required Access Type
+
+ PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
+ PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
+
+ Buffer - receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ Others TBS
+--*/
+
+
+NTSTATUS
+LsaSetInformationPolicy(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG InformationLevel,
+ IN PVOID Buffer
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaSetInformationPolicy API modifies information in the Policy Object.
+ The caller must have access appropriate to the information to be changed
+ in the Policy Object, see the InformationLevel parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationLevel - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
+ PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
+
+ Buffer - Points to a structure containing the information appropriate
+ to the information type specified by the InformationLevel parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+
+
+NTSTATUS
+LsaCreateAccount(
+ IN LSA_HANDLE PolicyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN PSID AccountSid,
+ OUT PLSA_HANDLE AccountHandle
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaCreateAccount API creates a new Account Object. The account will
+ be opened with the specified accesses granted. The caller must
+ have POLICY_CREATE_ACCOUNT access to the Policy Object.
+
+ Note that no verification is done to ensure the SID actually represents
+ a valid user, group or alias in any trusted domain.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ DesiredAccess - Specifies the accesses to be granted to the newly
+ created and opened account at this time.
+
+ AccountSid - Points to the SID of the account.
+
+ AccountHandle - Receives a handle referencing the newly created
+ account. This handle is used on subsequent accesses to the
+ account object.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_EXISTS - An account object having the given Sid
+ already exists and has been opened because LSA_OBJECT_OPEN_IF
+ disposition has been specified. This is a warning only.
+
+ STATUS_OBJECT_NAME_COLLISION - An account object having the given Sid
+ already exists but has not been opened because LSA_OBJECT_CREATE
+ disposition has been specified. This is an error.
+
+ STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
+--*/
+
+
+NTSTATUS
+LsaEnumerateAccounts(
+ IN LSA_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PVOID *EnumerationBuffer,
+ IN ULONG PreferedMaximumLength,
+ OUT PULONG CountReturned
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaEnumerateAccountsInLsa API returns information about the accounts
+ in the target system's LsaDatabase object. This call requires
+ LSA_ENUMERATE_ACCOUNTS access to the LsaDatabase object. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information. To
+ support this feature, the caller is provided with a handle that can
+ be used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationBuffer - Receives a pointer to a buffer containing enumeration
+ information. This consists of a header containing the count of
+ entries returned and a pointer to an array of enumeration information
+ entries for each object enumerated on this call. Currently, the
+ only information returned is the object Sids. These Sids may be
+ used together with object type to open the objects.
+
+ PreferedMaximumLength - prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+ CountReturned - Pointer to variable which will receive a count of the
+ entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if there are no more objects to enumerate. Note that
+ one or more objects may be enumerated on a call that returns this
+ reply.
+--*/
+
+NTSTATUS
+LsarCreateTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN PLSA_TRUST_INFORMATION TrustedDomainInformation,
+ OUT PLSA_HANDLE TrustedDomainHandle
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaCreateTrustedDomain API creates a new TrustedDomain object. The
+ caller must have POLICY_TRUST_ADMIN access to the Policy Object.
+
+ Note that NO verification is done to check that the given domain name
+ matches the given SID or that the SID or name represent an actual domain.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ DesiredAccess - Specifies the accesses to be granted for the newly
+ created object.
+
+ TrustedDomainInformation - Pointer to structure containing the name and
+ SID of the new Trusted Domain.
+
+ TrustedDomainHandle - receives a handle referencing the newly created
+ object. This handle is used on subsequent accesses to the object.
+
+--*/
+
+NTSTATUS
+LsaEnumerateTrustedDomains(
+ IN LSA_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PVOID *EnumerationBuffer,
+ IN ULONG PreferedMaximumLength,
+ OUT PULONG CountReturned
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaEnumerateTrustedDomains API returns information about the accounts
+ in the target system's Policy object. This call requires
+ POLICY_VIEW_LOCAL_INFORMATION access to the Policy object. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information. To
+ support this feature, the caller is provided with a handle that can
+ be used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationBuffer - Receives a pointer to a buffer containing enumeration
+ information. This consists of a header containing the count of
+ entries returned and a pointer to an array of enumeration information
+ entries for each object enumerated on this call.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+ CountReturned - Pointer to variable which will receive a count of the
+ entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if there are no more objects to enumerate. Note that
+ one or more objects may be enumerated on a call that returns this
+ reply.
+--*/
+
+NTSTATUS
+LsaLookupNames(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG Count,
+ IN PUNICODE_STRING Names,
+ OUT PSID **Sids
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaLookupNamesInLsa API attempts to translate names to Sids.
+ Permitted names include members of the current Primary Domain
+ associated with the target LSA and Well-Known names in the target
+ LSA such as the names of the Built-In accounts.
+
+Arguments:
+
+ This function is the LSA server RPC worker routine for the
+ LsaLookupNamesInLsa API.
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ Count - Specifies the number of names to be translated.
+
+ Names - Pointer to an array of Unicode String structures, each one
+ initialized to point to a Unicode name whose Sid is desired.
+ The names may include those of user or group accounts that are members
+ of the Primary Domain associated with the LSA, and Built-In Accounts.
+
+ Sids - Receives a pointer to an array of pointers to Sids corresponding
+ to the names given. These pointers appear in the same order as
+ their corresponding names appear in the Names array. If a name
+ cannot be translated, a NULL pointer is returned and a warning
+ is given that not all name could be translated.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
+ not be mapped. This is a warning only.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the call.
+
+ WARNING: This routine allocates memory for its output. The caller is
+ responsible for freeing this memory after use. See description of the
+ Sids parameter.
+
+--*/
+
+NTSTATUS
+LsaLookupSids(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG Count,
+ IN PSID *Sids,
+ OUT PUNICODE_STRING *Names
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaLookupSidsInLsa API attempts to find names corresponding to Sids.
+ The Sids may specify user or group accounts in one or more domains
+ or built-in accounts in the target LSA. Unicode String Names
+ corresponding to the Sids are returned. The Unicode names themselves are
+ returned in a buffer a pointer to which is returned in Buffer, and each
+ Unicode String structure is set to reference the corresponding name. If
+ a Sid cannot be translated to a name, the corresponding Unicode String
+ structure within the Names array is initialized to reference the full Sid
+ in textual form (see the description of Names below). The LSA_LOOKUP_SIDS
+ access type is required to use this service.
+
+ WARNING: This routine allocates memory for its output. The caller is
+ responsible for freeing this memory after use. See description of the
+ Names parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ Count - Specifies the count of entries to be translated.
+
+ Sids - Pointer to an array of pointers to Sids to be looked up and mapped
+ to names. The Sids may include those of user or group accounts,
+ built-in accounts and domains.
+
+ Names - Receives a pointer to array of Count UNICODE_STRINGs that will be filled in.
+ The nth pointer within these data structures will be set upon
+ return to point to the name corresonding to the nth relative
+ id. Each name string will be in a separate block of memory
+ allocated by this routine. When these names are no longer
+ needed, the caller is responsible for deallocating all of the memory.
+ This deallocation must be done by a single call to LsaFreeMemory()
+ specifiying the returned array pointer.
+
+ If a Sid is not recognized as a well known Sid or is not known to the
+ SAM Database for the Primary Domain, the entire Sid is converted to a
+ character text form and is referenced by the appropriate Unicode
+ String within the Names parameter. The character representation
+ has the form
+
+ S-<Revision>-<Identifier Authority>-<Subauthority 1>-...<Subauth. n>
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_SOME_NOT_MAPPED - Some or all of the names provided could not be
+ mapped. This is a warning only.
+--*/
+
+NTSTATUS
+LsaCreateSecret(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING SecretName,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE SecretHandle
+ );
+
+/*++
+
+Routine Description:
+
+ The LsaCreateSecret API creates a named Secret object in the
+ Lsa Database. Each Secret Object can have two values assigned,
+ called the Current Value and the Old Value. The meaning of these
+ values is known to the Secret object creator. The caller must have
+ LSA_CREATE_SECRET access to the LsaDatabase object.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ SecretName - Pointer to Unicode String specifying the name of the
+ secret.
+
+ DesiredAccess - Specifies the accesses to be granted to the newly
+ created and opened secret.
+
+ SecretHandle - Receives a handle to the newly created and opened
+ Secret object. This handle is used on subsequent accesses to
+ the object until closed.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_COLLISION - A Secret object having the given name
+ already exists.
+
+ STATUS_TOO_MANY_SECRETS - The maximum number of Secret objects in the
+ system has been reached.
+--*/
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Account Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsaOpenAccount(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE AccountHandle
+ );
+
+NTSTATUS
+LsaEnumeratePrivilegesOfAccount(
+ IN LSA_HANDLE AccountHandle,
+ OUT PPRIVILEGE_SET *Privileges
+ );
+
+NTSTATUS
+LsaAddPrivilegesToAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN PPRIVILEGE_SET Privileges
+ );
+
+NTSTATUS
+LsaRemovePrivilegesFromAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN BOOLEAN AllPrivileges,
+ IN PPRIVILEGE_SET Privileges
+ );
+
+NTSTATUS
+LsaGetQuotasForAccount(
+ IN LSA_HANDLE AccountHandle,
+ OUT PQUOTA_LIMITS QuotaLimits
+ );
+
+NTSTATUS
+LsaSetQuotasForAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN PQUOTA_LIMITS QuotaLimits
+ );
+
+NTSTATUS
+LsaGetSystemAccessAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN PULONG SystemAccess
+ );
+
+NTSTATUS
+LsaSetSystemAccessAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN PULONG SystemAccess
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Trusted Domain Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsaOpenTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN PSID TrustedDomainSid,
+ OUT PLSA_HANDLE TrustedDomainHandle
+ );
+
+NTSTATUS
+LsaQueryInfoTrustedDomain(
+ IN LSA_HANDLE TrustedDomainHandle,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ OUT PVOID *Buffer
+ );
+
+NTSTATUS
+LsaSetInformationTrustedDomain(
+ IN LSA_HANDLE TrustedDomainHandle,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID Buffer
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Secret Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsaOpenSecret(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING SecretName,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE SecretHandle
+ );
+
+NTSTATUS
+LsaSetSecret(
+ IN LSA_HANDLE SecretHandle,
+ IN OPTIONAL PUNICODE_STRING CurrentValue,
+ IN OPTIONAL PUNICODE_STRING OldValue
+ );
+
+NTSTATUS
+LsaQuerySecret(
+ IN LSA_HANDLE SecretHandle,
+ OUT OPTIONAL PUNICODE_STRING *CurrentValue,
+ OUT OPTIONAL PLARGE_INTEGER CurrentValueSetTime,
+ OUT OPTIONAL PUNICODE_STRING *OldValue,
+ OUT OPTIONAL PLARGE_INTEGER OldValueSetTime
+ );
+
+#endif // _NTLSA_
+ \ No newline at end of file
diff --git a/private/lsa/inc/rpcutil.h b/private/lsa/inc/rpcutil.h
new file mode 100644
index 000000000..566e9de32
--- /dev/null
+++ b/private/lsa/inc/rpcutil.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1990,91 Microsoft Corporation
+
+Module Name:
+
+ rpcutil.h
+
+Abstract:
+
+ This file contains prototypes for the bind and unbind functions that
+ all net api stub functions will call. It also includes the allocate
+ and free routines used by the MIDL generated RPC stubs.
+
+Author:
+
+ Dan Lafferty danl 06-Feb-1991
+ Scott Birrell (ScottBi) April 30, 1991 - LSA Version
+
+[Environment:]
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _RPCUTIL_
+#define _RPCUTIL_
+
+#ifndef RPC_NO_WINDOWS_H // Don't let rpc.h include windows.h
+#define RPC_NO_WINDOWS_H
+#endif // RPC_NO_WINDOWS_H
+
+#include <rpc.h>
+
+//
+// DEFINES
+//
+
+// BUGBUG - Change these when RPC uses Win32 APIs for named pipes.
+//
+#define LOCAL_NMPIPE_NAME TEXT("\\Device\\Namedpipe\\")
+#define REMOTE_NMPIPE_NAME TEXT("\\Device\\LanmanRedirector\\")
+#define NT_PIPE_PREFIX TEXT("\\PIPE\\")
+
+
+//
+// Function Prototypes
+//
+
+void *
+MIDL_user_allocate(
+ IN ULONG NumBytes
+ );
+
+void
+MIDL_user_free(
+ IN PVOID MemPointer
+ );
+
+
+RPC_STATUS
+LsapBindRpc(
+ IN PLSAPR_SERVER_NAME ServerName,
+ OUT RPC_BINDING_HANDLE * pBindingHandle
+ );
+
+RPC_STATUS
+LsapUnbindRpc(
+ RPC_BINDING_HANDLE BindingHandle
+ );
+
+
+
+#endif // _RPCUTIL_
diff --git a/private/lsa/lsacli.acf b/private/lsa/lsacli.acf
new file mode 100644
index 000000000..8c79d377b
--- /dev/null
+++ b/private/lsa/lsacli.acf
@@ -0,0 +1,90 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsacli.acf
+
+Abstract:
+
+ Local Security Authority CLIENT rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the client stubs for remotable LSA functions. The
+ definitions in this file qualify the information in lsarpc.idl.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LSA CLIENT STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use lsasrv.acf when generating server stubs.
+
+
+
+
+ The client likes to have all returned data in a single block of
+ allocated memory. This allows them to free the returned information
+ with a single call, rather than walking down some random tree of
+ allocated blocks.
+
+
+Author:
+
+ Scott Birrell (ScottBi) September 5, 1991
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+// BUGBUG mikemon : implicit handle to get around a midl bug
+[implicit_handle(handle_t IgnoreThisHandle)]
+
+interface lsarpc
+
+{
+
+//
+// define complex [out] parameters to be [allocate(all_nodes)]...
+//
+
+typedef [allocate(all_nodes)] PLSAPR_CR_CIPHER_VALUE;
+
+// typedef [allocate(all_nodes)] PLSAPR_ACCOUNT_ENUM_BUFFER;
+
+typedef [allocate(all_nodes)] PLSAPR_ACCOUNT_INFORMATION;
+
+typedef [allocate(all_nodes)] PLSAPR_TRUST_INFORMATION;
+
+typedef [allocate(all_nodes)] PLSA_TRANSLATED_SID;
+
+typedef [allocate(all_nodes)] PLSAPR_TRANSLATED_NAME;
+
+typedef [allocate(all_nodes)] PLSAPR_POLICY_INFORMATION;
+
+typedef [allocate(all_nodes)] PLSAPR_TRUSTED_DOMAIN_INFO;
+
+// typedef [allocate(all_nodes)] PLSAPR_TRUSTED_ENUM_BUFFER;
+
+typedef [allocate(all_nodes)] PLSAPR_REFERENCED_DOMAIN_LIST;
+
+// typedef [allocate(all_nodes)] PLSAPR_PRIVILEGE_ENUM_BUFFER;
+
+typedef [allocate(all_nodes)] PLSAPR_POLICY_PRIVILEGE_DEF;
+
+//
+// The following types were added for nt3.51 - PPC release
+//
+
+typedef [allocate(all_nodes)] PLSAPR_TRUSTED_PASSWORD_INFO;
+
+typedef [allocate(all_nodes)] PLSAPR_UNICODE_STRING_ARRAY;
+
+}
diff --git a/private/lsa/lsaimp.idl b/private/lsa/lsaimp.idl
new file mode 100644
index 000000000..05d17b254
--- /dev/null
+++ b/private/lsa/lsaimp.idl
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsaimp.idl
+
+Abstract:
+
+ Temporary dummy IDL interface for ntos2.h.
+
+ This file contains a dummy RPC Interface Definition Language file for
+ ntos2.h. This allows the file ntos2.h to be presented to the RPC compiler
+ as an included file within an imported interface. This temporary measure
+ is necessary so that definitions and function prototypes within ntos2.h
+ and its descendants are presentable to midl in such a way that:
+
+ (a) Types are not generated in the output .h file generated by midl
+ (b) Function prototypes therein are not treated as belonging to the
+ IDL interface being compiled.
+
+Author: Scott Birrell (ScottBi) April 23, 1991
+
+Environment: User Mode
+
+Revision History:
+
+--*/
+[
+ uuid(12345678-1234-ABCD-EF00-0123456789AB),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ endpoint("mscn_np:[\pipe\lsarpc]")
+]
+
+interface lsaimp
+
+{
+
+#define MIDL_PASS "lsaimp.idl"
+#include <lsaimp.h>
+void LsaImpDummy();
+}
diff --git a/private/lsa/lsarpc.idl b/private/lsa/lsarpc.idl
new file mode 100644
index 000000000..4e2a8a65e
--- /dev/null
+++ b/private/lsa/lsarpc.idl
@@ -0,0 +1,1066 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsarpc.idl
+
+Abstract:
+
+ Local Security Authority RPC Interface Definition File
+
+ This file contains the RPC Interface Definition Language file for
+ the LSA. This file includes all of the prototypes for the LSA functions
+ that are callable via RPC. These functions are internal versions of API
+ and are NOT visible to clients of the LSA. An LSA client calls the LSA
+ API defined in file ntlsa.h. These API are wrappers which call client
+ RPC stubs generated from this idl file by the RPC compiler.
+
+Author:
+
+ Scott Birrell (ScottBi) April 23, 1991
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ uuid(12345778-1234-ABCD-EF00-0123456789AB),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+interface lsarpc
+
+{
+
+//
+// Import a dummy interface containing #includes for public .h files. This
+// trick is necessary so that midl will only generate marshalling routines
+// for subtypes that are relevant to the parameters specified on the RPC
+// interface. midl also ingores function prototypes contained therein.
+//
+
+import "lsaimp.idl" ;
+
+//
+// BUGBUG ScottBi - The parens have to be omitted from the operand
+// because midl grammar does not support them.
+//
+
+#define LSAPR_DB_AUDIT_EVENT_COUNT SeMaxAuditType + 1
+
+//
+// LSA Generic Handle used to bind from client to server.
+// This handle is used for both LOCAL and REMOTE services.
+//
+
+typedef [handle] LPWSTR PLSAPR_SERVER_NAME, *PPLSAPR_SERVER_NAME;
+
+//
+// LSA RPC Context Handle (Internal definition of LSAPR_HANDLE)
+//
+
+typedef [context_handle] PVOID LSAPR_HANDLE;
+
+typedef LSAPR_HANDLE *PLSAPR_HANDLE;
+
+//
+// RPC definition of the SID structure. Note the use of the [size_is()]
+// qualifier to specify the number of elements in the variable size
+// imbedded SubAuthorityCount array at runtime.
+//
+//
+
+#pragma warning(disable:4200)
+
+typedef struct _LSAPR_SID {
+ UCHAR Revision;
+ UCHAR SubAuthorityCount;
+ SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
+ [size_is(SubAuthorityCount)] ULONG SubAuthority[*];
+} LSAPR_SID, *PLSAPR_SID, **PPLSAPR_SID;
+
+#pragma warning(default:4200)
+
+//
+// The following structure is used to identify an Account Object.
+//
+
+typedef struct _LSAPR_SID_INFORMATION {
+
+ PLSAPR_SID Sid;
+
+} LSAPR_SID_INFORMATION, *PLSAPR_SID_INFORMATION;
+
+//
+// The following structure is used to hold an array of Sids.
+//
+
+typedef struct _LSAPR_SID_ENUM_BUFFER {
+
+ ULONG Entries;
+ [size_is(Entries)] PLSAPR_SID_INFORMATION SidInfo;
+
+} LSAPR_SID_ENUM_BUFFER, *PLSAPR_SID_ENUM_BUFFER;
+
+//
+// The following structure is used to identify an Account Object.
+//
+
+typedef struct _LSAPR_ACCOUNT_INFORMATION {
+
+ PLSAPR_SID Sid;
+
+} LSAPR_ACCOUNT_INFORMATION, *PLSAPR_ACCOUNT_INFORMATION;
+
+//
+//
+// Account Object Enumeration Buffer
+//
+
+typedef struct _LSAPR_ACCOUNT_ENUM_BUFFER {
+
+ ULONG EntriesRead;
+ [size_is(EntriesRead)] PLSAPR_ACCOUNT_INFORMATION Information;
+
+} LSAPR_ACCOUNT_ENUM_BUFFER, *PLSAPR_ACCOUNT_ENUM_BUFFER;
+
+//
+// BUGBUG ScottBi - Someday these should be an imported interface.
+//
+// Unicode strings are counted 16-bit character strings.
+// The Length field and MaximumLength fields specify number of bytes,
+// (not wide-characters) in the string. So, this definition differs
+// a bit from the real unicode string type.
+//
+
+
+typedef struct _LSAPR_UNICODE_STRING {
+
+ USHORT Length;
+ USHORT MaximumLength;
+
+// BUGBUG - ScottBi - MIDL will raise an exception if Length is 0.
+// Wrappers passing UNICODE STRINGS must pass NULL if string is length
+// zero
+
+ [size_is(MaximumLength/2), length_is(Length/2)] PWSTR Buffer;
+
+} LSAPR_UNICODE_STRING, *PLSAPR_UNICODE_STRING;
+
+
+//
+// ANSI counted string
+//
+
+typedef struct _LSAPR_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+// [size_is(MaximumLength), length_is(Length)] PCHAR Buffer;
+ [size_is(MaximumLength)] PCHAR Buffer;
+} LSAPR_STRING, *PLSAPR_STRING, LSAPR_ANSI_STRING, *PLSAPR_ANSI_STRING;
+
+//
+// RPC definition of an ACL. This must be manually maintained to be the same
+// as the real ACL definition in ntseapi.h
+//
+
+#pragma warning(disable:4200)
+
+typedef struct _LSAPR_ACL {
+ UCHAR AclRevision;
+ UCHAR Sbz1;
+ USHORT AclSize;
+ [size_is(AclSize - 4)] UCHAR Dummy1[*];
+} LSAPR_ACL, *PLSAPR_ACL;
+
+#pragma warning(default:4200)
+
+//
+// RPC'able security descriptor definition.
+//
+
+typedef struct _LSAPR_SECURITY_DESCRIPTOR {
+
+ UCHAR Revision;
+ UCHAR Sbz1;
+ SECURITY_DESCRIPTOR_CONTROL Control;
+ PLSAPR_SID Owner;
+ PLSAPR_SID Group;
+ PLSAPR_ACL Sacl;
+ PLSAPR_ACL Dacl;
+
+} LSAPR_SECURITY_DESCRIPTOR, *PLSAPR_SECURITY_DESCRIPTOR;
+
+//
+// RPC'able Self-Relative Security Descriptor Definition.
+//
+
+typedef struct _LSAPR_SR_SECURITY_DESCRIPTOR {
+
+ ULONG Length;
+ [size_is(Length)] PUCHAR SecurityDescriptor;
+
+} LSAPR_SR_SECURITY_DESCRIPTOR, *PLSAPR_SR_SECURITY_DESCRIPTOR;
+
+typedef struct _LSAPR_LUID_AND_ATTRIBUTES {
+ OLD_LARGE_INTEGER Luid;
+ ULONG Attributes;
+} LSAPR_LUID_AND_ATTRIBUTES, * PLSAPR_LUID_AND_ATTRIBUTES;
+
+//
+// Privilege Set - This is defined for a privilege set of one.
+// If more than one privilege is needed, then this structure
+// will need to be allocated with more space.
+//
+// Note: don't change this structure without fixing the INITIAL_PRIVILEGE_SET
+// structure (defined in se.h)
+//
+
+#pragma warning(disable:4200)
+
+typedef struct _LSAPR_PRIVILEGE_SET {
+
+ ULONG PrivilegeCount;
+ ULONG Control;
+ [size_is(PrivilegeCount)] LSAPR_LUID_AND_ATTRIBUTES Privilege[*];
+
+} LSAPR_PRIVILEGE_SET, *PLSAPR_PRIVILEGE_SET, **PPLSAPR_PRIVILEGE_SET;
+
+#pragma warning(default:4200)
+
+
+//
+// The following data type is used to return information about privileges
+// defined on a system.
+//
+
+typedef struct _LSAPR_POLICY_PRIVILEGE_DEF {
+
+ LSAPR_UNICODE_STRING Name;
+ LUID LocalValue;
+
+} LSAPR_POLICY_PRIVILEGE_DEF, *PLSAPR_POLICY_PRIVILEGE_DEF;
+
+// where the members have the following usage:
+//
+// Name - Is the architected name of the privilege. This is the
+// primary key of the privilege and the only value that is
+// transportable between systems.
+//
+// Luid - is a LUID value assigned locally for efficient representation
+// of the privilege. Ths value is meaningful only on the system it
+// was assigned on and is not transportable in any way.
+//
+
+//
+// The following structure is used to hold an array of returned Privileges.
+//
+
+typedef struct _LSAPR_PRIVILEGE_ENUM_BUFFER {
+
+ ULONG Entries;
+ [size_is(Entries)] PLSAPR_POLICY_PRIVILEGE_DEF Privileges;
+
+} LSAPR_PRIVILEGE_ENUM_BUFFER, *PLSAPR_PRIVILEGE_ENUM_BUFFER;
+
+//
+// RPC'able Object Attributes structure for LSA use. Note that the
+// OBJECT_ATTRIBUTES structure is LSA-specific in that the RootDirectory
+// field is a handle of the specific type LSAPR_HANDLE.
+//
+// WARNING! This MUST be kept in sync with the corresponding structure in
+// ntdef.h! Structure should be moved to an imported interface when
+// possible.
+//
+
+typedef struct _LSAPR_OBJECT_ATTRIBUTES {
+
+ ULONG Length;
+ PUCHAR RootDirectory; // This field is not used
+ PSTRING ObjectName;
+ ULONG Attributes;
+ PLSAPR_SECURITY_DESCRIPTOR SecurityDescriptor;
+ PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+
+} LSAPR_OBJECT_ATTRIBUTES, *PLSAPR_OBJECT_ATTRIBUTES;
+
+//
+// Clear value structure
+//
+
+typedef struct _LSAPR_CR_CLEAR_VALUE {
+
+ ULONG Length;
+ ULONG MaximumLength;
+ [size_is(MaximumLength), length_is(Length)] PUCHAR Buffer;
+
+} LSAPR_CR_CLEAR_VALUE, *PLSAPR_CR_CLEAR_VALUE;
+
+//
+// Two-way encrypted value structure in Self-relative form. This
+// is just like a String.
+//
+
+typedef struct _LSAPR_CR_CIPHER_VALUE {
+
+ ULONG Length;
+ ULONG MaximumLength;
+ [size_is(MaximumLength), length_is(Length)] PUCHAR Buffer;
+
+} LSAPR_CR_CIPHER_VALUE, *PLSAPR_CR_CIPHER_VALUE;
+
+//
+// LSA API Internal Function prototypes for RPC interface. Each function
+// takes similar (but not always identical) parameters to its corresponding
+// exported API. Client and server stubs are generated for the functions
+// specified here. The client stubs are called by the wrapper functions
+// in file rpcapi.c in the lsa\client directory.
+//
+
+//
+// The following data type is used to identify a domain
+//
+
+typedef struct _LSAPR_TRUST_INFORMATION {
+
+ LSAPR_UNICODE_STRING Name;
+ PLSAPR_SID Sid;
+
+} LSAPR_TRUST_INFORMATION, *PLSAPR_TRUST_INFORMATION;
+
+// where members have the following usage:
+//
+// Name - The name of the domain.
+//
+// Sid - A pointer to the Sid of the Domain
+//
+
+//
+//
+// Trusted Domain Object Enumeration Buffer
+//
+
+typedef struct _LSAPR_TRUSTED_ENUM_BUFFER {
+
+ ULONG EntriesRead;
+ [size_is(EntriesRead)] PLSAPR_TRUST_INFORMATION Information;
+
+} LSAPR_TRUSTED_ENUM_BUFFER, *PLSAPR_TRUSTED_ENUM_BUFFER;
+
+
+//
+// The following data type is used in name and SID lookup services to
+// describe the domains referenced in the lookup operation.
+//
+
+typedef struct _LSAPR_REFERENCED_DOMAIN_LIST {
+
+ ULONG Entries;
+ [size_is(Entries)] PLSAPR_TRUST_INFORMATION Domains;
+ ULONG MaxEntries;
+
+} LSAPR_REFERENCED_DOMAIN_LIST, *PLSAPR_REFERENCED_DOMAIN_LIST;
+
+// where members have the following usage:
+//
+// Entries - Is a count of the number of domains described in the
+// Domains array.
+//
+// Domains - Is a pointer to an array of Entries LSA_TRUST_INFORMATION data
+// structures.
+//
+
+//
+// The following data type is used in Sid to name lookup services to
+// reference the list of domains referenced in the lookup operation
+//
+// BUGBUG - ScottBi - When midl supports the size_is operand in its
+// full form with more than one operand, this intermediate structure
+// can be eliminated.
+//
+
+typedef struct _LSAPR_TRANSLATED_SIDS {
+
+ ULONG Entries;
+ [size_is(Entries)] PLSA_TRANSLATED_SID Sids;
+
+} LSAPR_TRANSLATED_SIDS, *PLSAPR_TRANSLATED_SIDS;
+
+//
+// The following data type is used in SID to name lookup services to
+// describe the domains referenced in the lookup operation.
+//
+
+typedef struct _LSAPR_TRANSLATED_NAME {
+
+ SID_NAME_USE Use;
+ LSAPR_UNICODE_STRING Name;
+ LONG DomainIndex;
+
+} LSAPR_TRANSLATED_NAME, *PLSAPR_TRANSLATED_NAME;
+
+// where the members have the following usage:
+//
+// Use - Identifies the use of the name. If this value is SidUnknown
+// or SidInvalid, then the remainder of the record is not set and
+// should be ignored. If this value is SidWellKnownGroup then the
+// Name field is invalid, but the DomainIndex field is not.
+//
+// Name - Contains the isolated name of the translated SID.
+//
+// DomainIndex - Is the index of an entry in a related
+// LSA_REFERENCED_DOMAIN_LIST data structure describing the domain
+// in which the account was found.
+//
+// If there is no corresponding reference domain for an entry, then
+// this field will contain a negative value.
+//
+//
+// The following data type is used in Sid to name lookup services to
+// reference the list of domains referenced in the lookup operation
+//
+// BUGBUG - ScottBi - When midl supports the size_is operand in its
+// full form with more than one operand, this intermediate structure
+// can be eliminated.
+//
+
+typedef struct _LSAPR_TRANSLATED_NAMES {
+
+ ULONG Entries;
+ [size_is(Entries)] PLSAPR_TRANSLATED_NAME Names;
+
+} LSAPR_TRANSLATED_NAMES, *PLSAPR_TRANSLATED_NAMES;
+
+
+
+//
+// The following structure corresponds to the PolicyAccountDomainInformation
+// information class.
+//
+
+typedef struct _LSAPR_POLICY_ACCOUNT_DOM_INFO {
+
+ LSAPR_UNICODE_STRING DomainName;
+ PLSAPR_SID DomainSid;
+
+} LSAPR_POLICY_ACCOUNT_DOM_INFO, *PLSAPR_POLICY_ACCOUNT_DOM_INFO;
+
+// where the members have the following usage:
+//
+// DomainName - Is the name of the domain
+//
+// DomainSid - Is the Sid of the domain
+//
+
+
+//
+// The following structure corresponds to the PolicyPrimaryDomainInformation
+// information class.
+//
+
+typedef struct _LSAPR_POLICY_PRIMARY_DOM_INFO {
+
+ LSAPR_UNICODE_STRING Name;
+ PLSAPR_SID Sid;
+
+} LSAPR_POLICY_PRIMARY_DOM_INFO, *PLSAPR_POLICY_PRIMARY_DOM_INFO;
+
+// where the members have the following usage:
+//
+// Name - Is the name of the domain
+//
+// Sid - Is the Sid of the domain
+//
+
+
+//
+// The following structure corresponds to the PolicyPdAccountInformation
+// information class. This structure may be used in Query operations
+// only.
+//
+
+typedef struct _LSAPR_POLICY_PD_ACCOUNT_INFO {
+
+ LSAPR_UNICODE_STRING Name;
+
+} LSAPR_POLICY_PD_ACCOUNT_INFO, *PLSAPR_POLICY_PD_ACCOUNT_INFO;
+
+// where the members have the following usage:
+//
+// Name - Is the name of an account in the domain that should be used
+// for authentication and name/ID lookup requests.
+//
+
+
+//
+// The following structure corresponds to the PolicyReplicaSourceInformation
+// information class.
+//
+
+typedef struct _LSAPR_POLICY_REPLICA_SRCE_INFO {
+
+ LSAPR_UNICODE_STRING ReplicaSource;
+ LSAPR_UNICODE_STRING ReplicaAccountName;
+
+} LSAPR_POLICY_REPLICA_SRCE_INFO, *PLSAPR_POLICY_REPLICA_SRCE_INFO;
+
+
+typedef struct _LSAPR_POLICY_AUDIT_EVENTS_INFO {
+
+ BOOLEAN AuditingMode;
+ [size_is(MaximumAuditEventCount)] PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions;
+ ULONG MaximumAuditEventCount;
+
+} LSAPR_POLICY_AUDIT_EVENTS_INFO, *PLSAPR_POLICY_AUDIT_EVENTS_INFO;
+
+//
+// The following data type is used to hold Policy Information
+// of a given class.
+//
+
+typedef [switch_type(POLICY_INFORMATION_CLASS)] union
+
+_LSAPR_POLICY_INFORMATION {
+
+ [case(PolicyAuditLogInformation)] POLICY_AUDIT_LOG_INFO PolicyAuditLogInfo;
+ [case(PolicyAuditEventsInformation)] LSAPR_POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo;
+ [case(PolicyPrimaryDomainInformation)] LSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo;
+ [case(PolicyAccountDomainInformation)] LSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo;
+ [case(PolicyPdAccountInformation)] LSAPR_POLICY_PD_ACCOUNT_INFO PolicyPdAccountInfo;
+ [case(PolicyLsaServerRoleInformation)] POLICY_LSA_SERVER_ROLE_INFO PolicyServerRoleInfo;
+ [case(PolicyReplicaSourceInformation)] LSAPR_POLICY_REPLICA_SRCE_INFO PolicyReplicaSourceInfo;
+ [case(PolicyDefaultQuotaInformation)] POLICY_DEFAULT_QUOTA_INFO PolicyDefaultQuotaInfo;
+ [case(PolicyModificationInformation)] POLICY_MODIFICATION_INFO PolicyModificationInfo;
+ [case(PolicyAuditFullSetInformation)] POLICY_AUDIT_FULL_SET_INFO PolicyAuditFullSetInfo;
+ [case(PolicyAuditFullQueryInformation)] POLICY_AUDIT_FULL_QUERY_INFO PolicyAuditFullQueryInfo;
+
+} LSAPR_POLICY_INFORMATION;
+
+typedef LSAPR_POLICY_INFORMATION *PLSAPR_POLICY_INFORMATION;
+
+//
+// Account object type-specific Access Types
+//
+
+#define LSA_ACCOUNT_VIEW 0x00000001L
+#define LSA_ACCOUNT_ADJUST_PRIVILEGES 0x00000002L
+#define LSA_ACCOUNT_ADJUST_QUOTAS 0x00000004L
+#define LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS 0x00000008L
+
+#define LSA_ACCOUNT_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED | \
+ LSA_ACCOUNT_VIEW | \
+ LSA_ACCOUNT_ADJUST_PRIVILEGES | \
+ LSA_ACCOUNT_ADJUST_QUOTAS | \
+ LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS )
+
+//
+// The following data type corresponds to the TrustedDomainNameInformation
+// information class.
+//
+
+typedef struct _LSAPR_TRUSTED_DOMAIN_NAME_INFO {
+
+ LSAPR_UNICODE_STRING Name;
+
+} LSAPR_TRUSTED_DOMAIN_NAME_INFO, *PLSAPR_TRUSTED_DOMAIN_NAME_INFO;
+
+//
+// The following data type corresponds to the TrustedControllersInformation
+// information class.
+//
+
+typedef struct _LSAPR_TRUSTED_CONTROLLERS_INFO {
+
+ ULONG Entries;
+ [size_is(Entries)] PLSAPR_UNICODE_STRING Names;
+
+} LSAPR_TRUSTED_CONTROLLERS_INFO, *PLSAPR_TRUSTED_CONTROLLERS_INFO;
+
+
+
+// where members have the following meaning:
+//
+// Entries - Indicate how mamy entries there are in the Names array.
+//
+// Names - Pointer to an array of UNICODE_STRING structures containing the
+// names of domain controllers of the domain. This information may not
+// be accurate and should be used only as a hint. The order of this
+// list is considered significant and will be maintained.
+//
+// By convention, the first name in this list is assumed to be the
+// Primary Domain Controller of the domain. If the Primary Domain
+// Controller is not known, the first name should be set to the NULL
+// string.
+//
+
+
+//
+// The following data type corresponds to the TrustedPasswordInformation
+// information class.
+//
+
+typedef struct _LSAPR_TRUSTED_PASSWORD_INFO {
+
+ PLSAPR_CR_CIPHER_VALUE Password;
+ PLSAPR_CR_CIPHER_VALUE OldPassword;
+
+} LSAPR_TRUSTED_PASSWORD_INFO, *PLSAPR_TRUSTED_PASSWORD_INFO;
+
+// where members have the following meaning:
+//
+// Password - Contains new password for the trusted domain
+//
+// OldPassword - Optionally contains the old password for the trusted
+// domain.
+//
+
+
+//
+// The following data type is used to hold Trusted Domain Information
+// of a given class.
+//
+
+typedef [switch_type(TRUSTED_INFORMATION_CLASS)] union
+
+_LSAPR_TRUSTED_DOMAIN_INFO {
+
+ [case(TrustedDomainNameInformation)] LSAPR_TRUSTED_DOMAIN_NAME_INFO TrustedDomainNameInfo;
+ [case(TrustedControllersInformation)] LSAPR_TRUSTED_CONTROLLERS_INFO TrustedControllersInfo;
+ [case(TrustedPosixOffsetInformation)] TRUSTED_POSIX_OFFSET_INFO TrustedPosixOffsetInfo;
+ [case(TrustedPasswordInformation)] LSAPR_TRUSTED_PASSWORD_INFO TrustedPasswordInfo;
+
+} LSAPR_TRUSTED_DOMAIN_INFO;
+
+typedef LSAPR_TRUSTED_DOMAIN_INFO *PLSAPR_TRUSTED_DOMAIN_INFO;
+
+
+//
+// New types for NT3.51 - PPC release
+//
+
+
+typedef PLSAPR_UNICODE_STRING PLSAPR_UNICODE_STRING_ARRAY;
+
+typedef struct _LSAPR_USER_RIGHT_SET {
+ ULONG Entries;
+ [size_is(Entries)] PLSAPR_UNICODE_STRING_ARRAY UserRights;
+} LSAPR_USER_RIGHT_SET, *PLSAPR_USER_RIGHT_SET;
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Miscellaneous API function prototypes //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarClose(
+ [in,out] LSAPR_HANDLE *ObjectHandle
+ );
+
+//
+// This routine is being superseded by LsarDeleteObject. The reason is
+// that a pointer to a handle rather than a handle is essential so that
+// RPC on the server side can clean up. Because of the need for RPC
+// interface comaptibility, the new routine has been added at the end
+// of the interface,
+//
+
+NTSTATUS
+LsarDelete(
+ [in] LSAPR_HANDLE ObjectHandle
+ );
+
+NTSTATUS
+LsarEnumeratePrivileges(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in, out] PLSA_ENUMERATION_HANDLE EnumerationContext,
+ [out] PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
+ [in] ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsarQuerySecurityObject(
+ [in] LSAPR_HANDLE ObjectHandle,
+ [in] SECURITY_INFORMATION SecurityInformation,
+ [out] PLSAPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor
+ );
+
+NTSTATUS
+LsarSetSecurityObject(
+ [in] LSAPR_HANDLE ObjectHandle,
+ [in] SECURITY_INFORMATION SecurityInformation,
+ [in] PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor
+ );
+
+NTSTATUS
+LsarChangePassword(
+ [in] PLSAPR_UNICODE_STRING ServerName,
+ [in] PLSAPR_UNICODE_STRING DomainName,
+ [in] PLSAPR_UNICODE_STRING AccountName,
+ [in] PLSAPR_UNICODE_STRING OldPassword,
+ [in] PLSAPR_UNICODE_STRING NewPassword
+ );
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Policy Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarOpenPolicy(
+ [in,unique] PLSAPR_SERVER_NAME SystemName,
+ [in] PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *PolicyHandle
+ );
+
+NTSTATUS
+LsarQueryInformationPolicy(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] POLICY_INFORMATION_CLASS InformationClass,
+ [out, switch_is(InformationClass)]
+ PLSAPR_POLICY_INFORMATION *PolicyInformation
+ );
+
+NTSTATUS
+LsarSetInformationPolicy(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] POLICY_INFORMATION_CLASS InformationClass,
+ [in, switch_is(InformationClass)]
+ PLSAPR_POLICY_INFORMATION PolicyInformation
+ );
+
+NTSTATUS
+LsarClearAuditLog(
+ [in] LSAPR_HANDLE PolicyHandle
+ );
+
+NTSTATUS
+LsarCreateAccount(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID AccountSid,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *AccountHandle
+ );
+
+NTSTATUS
+LsarEnumerateAccounts(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] [out] PLSA_ENUMERATION_HANDLE EnumerationContext,
+ [out] PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer,
+ [in] ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsarCreateTrustedDomain(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_TRUST_INFORMATION TrustedDomainInformation,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *TrustedDomainHandle
+ );
+
+NTSTATUS
+LsarEnumerateTrustedDomains(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] [out] PLSA_ENUMERATION_HANDLE EnumerationContext,
+ [out] PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ [in] ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsarLookupNames(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] ULONG Count,
+ [in, size_is(Count)] PLSAPR_UNICODE_STRING Names,
+ [out] PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ [in, out] PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ [in] LSAP_LOOKUP_LEVEL LookupLevel,
+ [in, out] PULONG MappedCount
+ );
+
+NTSTATUS
+LsarLookupSids(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
+ [out] PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ [in, out] PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ [in] LSAP_LOOKUP_LEVEL LookupLevel,
+ [in, out] PULONG MappedCount
+ );
+
+NTSTATUS
+LsarCreateSecret(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_UNICODE_STRING SecretName,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *SecretHandle
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Account Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarOpenAccount(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID AccountSid,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *AccountHandle
+ );
+
+NTSTATUS
+LsarEnumeratePrivilegesAccount(
+ [in] LSAPR_HANDLE AccountHandle,
+ [out] PLSAPR_PRIVILEGE_SET *Privileges
+ );
+
+NTSTATUS
+LsarAddPrivilegesToAccount(
+ [in] LSAPR_HANDLE AccountHandle,
+ [in] PLSAPR_PRIVILEGE_SET Privileges
+ );
+
+NTSTATUS
+LsarRemovePrivilegesFromAccount(
+ [in] LSAPR_HANDLE AccountHandle,
+ [in] BOOLEAN AllPrivileges,
+ [in, unique] PLSAPR_PRIVILEGE_SET Privileges
+ );
+
+NTSTATUS
+LsarGetQuotasForAccount(
+ [in] LSAPR_HANDLE AccountHandle,
+ [out] PQUOTA_LIMITS QuotaLimits
+ );
+
+NTSTATUS
+LsarSetQuotasForAccount(
+ [in] LSAPR_HANDLE AccountHandle,
+ [in] PQUOTA_LIMITS QuotaLimits
+ );
+
+NTSTATUS
+LsarGetSystemAccessAccount(
+ [in] LSAPR_HANDLE AccountHandle,
+ [out] PULONG SystemAccess
+ );
+
+NTSTATUS
+LsarSetSystemAccessAccount(
+ [in] LSAPR_HANDLE AccountHandle,
+ [in] ULONG SystemAccess
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Trusted Domain Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarOpenTrustedDomain(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID TrustedDomainSid,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *TrustedDomainHandle
+ );
+
+NTSTATUS
+LsarQueryInfoTrustedDomain(
+ [in] LSAPR_HANDLE TrustedDomainHandle,
+ [in] TRUSTED_INFORMATION_CLASS InformationClass,
+ [out, switch_is(InformationClass)]
+ PLSAPR_TRUSTED_DOMAIN_INFO *TrustedDomainInformation
+ );
+
+NTSTATUS
+LsarSetInformationTrustedDomain(
+ [in] LSAPR_HANDLE TrustedDomainHandle,
+ [in] TRUSTED_INFORMATION_CLASS InformationClass,
+ [in, switch_is(InformationClass)]
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Secret Object API function prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarOpenSecret(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_UNICODE_STRING SecretName,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *SecretHandle
+ );
+
+NTSTATUS
+LsarSetSecret(
+ [in] LSAPR_HANDLE SecretHandle,
+ [in, unique] PLSAPR_CR_CIPHER_VALUE EncryptedCurrentValue,
+ [in, unique] PLSAPR_CR_CIPHER_VALUE EncryptedOldValue
+ );
+
+NTSTATUS
+LsarQuerySecret(
+ [in] LSAPR_HANDLE SecretHandle,
+ [in, out, unique] PLSAPR_CR_CIPHER_VALUE *EncryptedCurrentValue,
+ [in, out, unique] PLARGE_INTEGER CurrentValueSetTime,
+ [in, out, unique] PLSAPR_CR_CIPHER_VALUE *EncryptedOldValue,
+ [in, out, unique] PLARGE_INTEGER OldValueSetTime
+ );
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy - Privilege Object API Prototypes //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+
+NTSTATUS
+LsarLookupPrivilegeValue(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_UNICODE_STRING Name,
+ [out] PLUID Value
+ );
+
+
+NTSTATUS
+LsarLookupPrivilegeName(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLUID Value,
+ [out] PLSAPR_UNICODE_STRING *Name
+ );
+
+
+NTSTATUS
+LsarLookupPrivilegeDisplayName(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_UNICODE_STRING Name,
+ [in] SHORT ClientLanguage,
+ [in] SHORT ClientSystemDefaultLanguage,
+ [out] PLSAPR_UNICODE_STRING *DisplayName,
+ [out] PWORD LanguageReturned
+ );
+
+//
+// Important note:
+//
+// This routine will supersede LsarDelete. The difference is that, as on
+// LsarClose a pointer to a handle is required rather than a handle so that
+// LsarDeleteObject() can inform the RPC server calling stub that the handle
+// has been deleted by returning NULL.
+//
+
+NTSTATUS
+LsarDeleteObject(
+ [in,out] LSAPR_HANDLE *ObjectHandle
+ );
+
+
+//
+// These APIs are new for nt 3.51 (PPC release) and will be emulated when
+// talking to old servers.
+//
+
+NTSTATUS
+LsarEnumerateAccountsWithUserRight(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in,unique] PLSAPR_UNICODE_STRING UserRight,
+ [out] PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer
+ );
+
+NTSTATUS
+LsarEnumerateAccountRights(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID AccountSid,
+ [out] PLSAPR_USER_RIGHT_SET UserRights
+ );
+
+NTSTATUS
+LsarAddAccountRights(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID AccountSid,
+ [in] PLSAPR_USER_RIGHT_SET UserRights
+ );
+
+NTSTATUS
+LsarRemoveAccountRights(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID AccountSid,
+ [in] BOOLEAN AllRights,
+ [in] PLSAPR_USER_RIGHT_SET UserRights
+ );
+
+NTSTATUS
+LsarQueryTrustedDomainInfo(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID TrustedDomainSid,
+ [in] TRUSTED_INFORMATION_CLASS InformationClass,
+ [out, switch_is(InformationClass)]
+ PLSAPR_TRUSTED_DOMAIN_INFO * TrustedDomainInformation
+ );
+
+NTSTATUS
+LsarSetTrustedDomainInfo(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID TrustedDomainSid,
+ [in] TRUSTED_INFORMATION_CLASS InformationClass,
+ [in, switch_is(InformationClass)]
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
+ );
+
+NTSTATUS
+LsarDeleteTrustedDomain(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_SID TrustedDomainSid
+ );
+
+NTSTATUS
+LsarStorePrivateData(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_UNICODE_STRING KeyName,
+ [in,unique] PLSAPR_CR_CIPHER_VALUE EncryptedData
+ );
+
+NTSTATUS
+LsarRetrievePrivateData(
+ [in] LSAPR_HANDLE PolicyHandle,
+ [in] PLSAPR_UNICODE_STRING KeyName,
+ [in, out] PLSAPR_CR_CIPHER_VALUE *EncryptedData
+ );
+
+NTSTATUS
+LsarOpenPolicy2(
+ [in,unique,string] PLSAPR_SERVER_NAME SystemName,
+ [in] PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes,
+ [in] ACCESS_MASK DesiredAccess,
+ [out] LSAPR_HANDLE *PolicyHandle
+ );
+
+NTSTATUS
+LsarGetUserName(
+ [in,unique,string] PLSAPR_SERVER_NAME SystemName,
+ [in,out] PLSAPR_UNICODE_STRING * UserName,
+ [in,out,unique] PLSAPR_UNICODE_STRING * DomainName
+ );
+
+}
diff --git a/private/lsa/lsasrv.acf b/private/lsa/lsasrv.acf
new file mode 100644
index 000000000..45bdf0e2f
--- /dev/null
+++ b/private/lsa/lsasrv.acf
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ Lsasrv.acf
+
+Abstract:
+
+ Security Account Manager SERVER rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the server stubs for remotable LSA functions. The
+ definitions in this file qualify the information in Lsarpc.idl.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LSA SERVER STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use Lsacli.acf when generating client stubs.
+
+
+
+
+ The server likes to have all passed data in a single block of
+ allocated memory. This allows it to free the returned information
+ with a single call, rather than walking down some random tree of
+ allocated blocks.
+
+
+
+Author:
+
+ Scott Birrell (ScottBi) September 5, 1991
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+// BUGBUG : implicit handle to get around a midl bug
+[implicit_handle(handle_t IgnoreThisHandle)]
+
+interface lsarpc
+
+{
+
+
+//
+// define complex [in] parameters to be [allocate(all_nodes)]...
+//
+
+typedef [allocate(all_nodes)] PLSAPR_CR_CIPHER_VALUE;
+
+}
+ \ No newline at end of file
diff --git a/private/lsa/makefil0 b/private/lsa/makefil0
new file mode 100644
index 000000000..227e60420
--- /dev/null
+++ b/private/lsa/makefil0
@@ -0,0 +1,85 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = lsarpc
+CLIENT_ACF = lsacli.acf
+SERVER_ACF = lsasrv.acf
+
+CLIENT_INC_FILE = $(IDL_NAME)_c.h
+SERVER_INC_FILE = $(IDL_NAME).h
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = ..\inc
+LSAINC = .\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(LSAINC)
+
+EXTRN_DEPENDS = $(SDKINC)\ntlsa.h
+
+CLIENT_FLAGS = -Oi -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(CLIENT_ACF) -header $(CLIENT_INC_FILE)
+SERVER_FLAGS = -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(SERVER_ACF) -header $(SERVER_INC_FILE)
+
+CPP = -cpp_cmd "$(MIDL_CPP)"
+
+#
+# Separate client and server targets. Note that the .h file produced
+# when MIDL is run with the client .acf file attached differs from the
+# .h file produced when MIDL is run with the server .acf file attached.
+#
+
+CLIENT_TARGETS = uclient\$(IDL_NAME)_c.c \
+ uclient\$(CLIENT_INC_FILE)
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c \
+ $(PRIVINC)\$(SERVER_INC_FILE)
+
+TARGETS = $(CLIENT_TARGETS) \
+ $(SERVER_TARGETS)
+
+CLIENT_EXTRN_DEPENDS = $(CLIENT_ACF)
+SERVER_EXTRN_DEPENDS = $(SERVER_ACF)
+EXTRN_DEPENDS = $(CLIENT_EXTRN_DEPENDS)
+#EXTRN_DEPENDS = $(CLIENT_EXTRN_DEPENDS) $(SERVER_EXTRN_DEPENDS)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delete_source all
+
+delete_source:
+ -erase $(TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : $(IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS)
+ IF EXIST inc\$(IDL_NAME).h del inc\$(IDL_NAME).h
+ copy $(PRIVINC)\lsaimp.h .
+ midl $(CPP) $(CLIENT_FLAGS) $(IDL_NAME).idl $(INCS)
+ del lsaimp.h
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\uclient & del $(IDL_NAME)_c.c
+ IF EXIST $(IDL_NAME)_s.c del $(IDL_NAME)_s.c
+ IF EXIST $(CLIENT_INC_FILE) copy $(CLIENT_INC_FILE) .\uclient & del $(CLIENT_INC_FILE)
+
+#$(SERVER_TARGETS) : $(IDL_NAME).idl $(SERVER_EXTRN_DEPENDS)
+$(SERVER_TARGETS) : $(IDL_NAME).idl
+ IF EXIST inc\$(IDL_NAME).h del inc\$(IDL_NAME).h
+ copy $(PRIVINC)\lsaimp.h
+ midl $(CPP) $(SERVER_FLAGS) $(IDL_NAME).idl $(INCS)
+ del lsaimp.h
+ IF EXIST $(IDL_NAME)_c.c del $(IDL_NAME)_c.c
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(SERVER_INC_FILE) copy $(SERVER_INC_FILE) $(PRIVINC) & del $(SERVER_INC_FILE)
diff --git a/private/lsa/msprivs/makefile b/private/lsa/msprivs/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/msprivs/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/msprivs/mspr_rev.rc b/private/lsa/msprivs/mspr_rev.rc
new file mode 100644
index 000000000..1d3ea70fa
--- /dev/null
+++ b/private/lsa/msprivs/mspr_rev.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft Privilege Translations"
+#define VER_INTERNALNAME_STR "mspriv.dll"
+
+#include "common.ver"
+RCINCLUDE msprivs2.rc
+
diff --git a/private/lsa/msprivs/msprivs.def b/private/lsa/msprivs/msprivs.def
new file mode 100644
index 000000000..f83f63ca1
--- /dev/null
+++ b/private/lsa/msprivs/msprivs.def
@@ -0,0 +1,7 @@
+LIBRARY msprivs
+
+DESCRIPTION 'Displayable Privilege Names For Microsoft Defined Privileges'
+
+EXPORTS
+ MsPrivsDummyEntry
+
diff --git a/private/lsa/msprivs/msprivs.xls b/private/lsa/msprivs/msprivs.xls
new file mode 100644
index 000000000..0f34d8d0c
--- /dev/null
+++ b/private/lsa/msprivs/msprivs.xls
Binary files differ
diff --git a/private/lsa/msprivs/msprivs2.rc b/private/lsa/msprivs/msprivs2.rc
new file mode 100644
index 000000000..322b4a427
--- /dev/null
+++ b/private/lsa/msprivs/msprivs2.rc
Binary files differ
diff --git a/private/lsa/msprivs/mstmp.c b/private/lsa/msprivs/mstmp.c
new file mode 100644
index 000000000..d3c223ac5
--- /dev/null
+++ b/private/lsa/msprivs/mstmp.c
@@ -0,0 +1,58 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tmp.c
+
+Abstract:
+
+ Temporary (unnecessary) DLL entry point routine.
+
+
+ The entry in this file is a bit of a hack. The code isn't
+ needed, but our linker doesn't know how to build a dll with data only.
+ When MikeOl gets this fixed, we can remove this obligatory source
+ file.
+
+
+Author:
+
+ Jim Kelly 24-Mar-1992
+
+Revision History:
+
+--*/
+
+
+#include <nt.h>
+
+BOOLEAN
+MsPrivsDummyEntry(
+ IN PVOID DllHandle,
+ IN ULONG Reason,
+ IN PCONTEXT Context)
+
+/*++
+
+Routine Description:
+
+ This routine gets called when this DLL is loaded by the loader.
+ It does nothing and wouldn't be needed if the linker worked
+ correctly.
+
+Arguments:
+
+ None.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ return TRUE;
+}
diff --git a/private/lsa/msprivs/sources b/private/lsa/msprivs/sources
new file mode 100644
index 000000000..d50b0bc38
--- /dev/null
+++ b/private/lsa/msprivs/sources
@@ -0,0 +1,67 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=msprivs
+
+TARGETNAME=msprivs
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+
+TARGETLIBS=
+
+TARGETTYPE=DYNLINK
+
+#
+# The following entry information is a bit of a hack. The code isn't
+# needed, but our linker doesn't know how to build a dll with data only.
+# When MikeOl gets this fixed, we can remove the code and this obligatory
+# entry and base information. By the way, the base choice is just one I
+# know isn't used elsewhere in the system.
+#
+
+DLLBASE=@$(BASEDIR)\public\sdk\lib\coffbase.txt,lsaap
+DLLENTRY=MsPrivsDummyEntry
+RCCODEPAGE=1252
+
+
+INCLUDES=.;..\inc;..\..\inc
+
+SOURCES= \
+ mspr_rev.rc \
+ mstmp.c
+
+
+UMRES=obj\*\mspr_rev.res
+
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
diff --git a/private/lsa/msprivs/tprivs.c b/private/lsa/msprivs/tprivs.c
new file mode 100644
index 000000000..8e0e90e12
--- /dev/null
+++ b/private/lsa/msprivs/tprivs.c
@@ -0,0 +1,581 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ tprivs.c
+
+Abstract:
+
+ Test privilege lookup services and ms privilege resource file.
+
+Author:
+
+ Jim Kelly (JimK) 26-Mar-1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#define UNICODE
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h> // needed for winbase.h
+#include <rpc.h> // DataTypes and runtime APIs
+#include <windows.h> // LocalAlloc
+#include <ntlsa.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ntrpcp.h> // prototypes for MIDL user functions
+
+
+
+
+
+#define EQUAL_LUID( L1, L2 ) \
+ ( ((L1)->HighPart == (L2)->HighPart) && \
+ ((L1)->LowPart == (L2)->LowPart) )
+
+
+#define printfLuid( L ) \
+ printf("[%1d, %2d]", (L)->HighPart, (L)->LowPart)
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Module-wide data types //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+typedef struct _KNOWN_PRIVILEGE {
+ LUID Luid;
+ UNICODE_STRING ProgrammaticName;
+} KNOWN_PRIVILEGE, *PKNOWN_PRIVILEGE;
+
+typedef struct _TPRIV_LANGUAGE {
+ USHORT Id;
+ PWSTR Name;
+} TPRIV_LANGUAGE, *PTPRIV_LANGUAGE;
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Module-wide variables //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+//
+// name of target LSA system
+//
+
+PUNICODE_STRING SystemName = NULL;
+
+//
+// Test level
+//
+
+int Level;
+
+//
+// Handle to LSA Policy object
+//
+
+LSA_HANDLE PolicyHandle = NULL;
+
+
+ULONG KnownPrivilegeCount;
+KNOWN_PRIVILEGE KnownPrivilege[SE_MAX_WELL_KNOWN_PRIVILEGE];
+
+
+
+//
+// So that we can test each language
+//
+
+TPRIV_LANGUAGE Language[] = {
+ {MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL ), L"English, Neutral"},
+ {MAKELANGID( LANG_FRENCH, SUBLANG_NEUTRAL ), L"French, Neutral"},
+ {MAKELANGID( LANG_GERMAN, SUBLANG_NEUTRAL ), L"German, Neutral"},
+ {MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ), L"Spanish, Neutral"},
+ {MAKELANGID( LANG_DUTCH, SUBLANG_NEUTRAL ), L"Dutch, Neutral"},
+ {MAKELANGID( LANG_ITALIAN, SUBLANG_NEUTRAL ), L"Italian, Neutral"},
+ {MAKELANGID( LANG_DANISH, SUBLANG_NEUTRAL ), L"Danish, Neutral"},
+ {MAKELANGID( LANG_FINNISH, SUBLANG_NEUTRAL ), L"Finnish, Neutral"},
+ {MAKELANGID( LANG_NORWEGIAN, SUBLANG_NEUTRAL ), L"Norweigian, Neutral"},
+ {MAKELANGID( LANG_SWEDISH, SUBLANG_NEUTRAL ), L"Swedish, Neutral"},
+ {MAKELANGID( LANG_PORTUGUESE, SUBLANG_NEUTRAL ), L"Portuguese, Neutral"},
+ {0, L""} // End of array
+ };
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Routine prototypes //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+
+NTSTATUS
+TestInitialize();
+
+NTSTATUS
+TestPrivilegeLookup();
+
+NTSTATUS
+TestLookupProgramName();
+
+NTSTATUS
+TestLookupDisplayName();
+
+NTSTATUS
+TestLookupValue();
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Routines //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+
+
+VOID
+main (argc, argv)
+int argc;
+char **argv;
+
+{
+ ANSI_STRING ServerNameAnsi;
+ UNICODE_STRING SystemNameU;
+ int Index;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+
+ SystemName = NULL;
+
+ if ((argc < 1) || (argc > 2)) {
+
+ printf("Usage: tprivs [\\servername]");
+ return;
+ }
+
+ //
+ // Parse the parameters (if any). Assume that a parameter beginning
+ // \\ is the server name and a parameter beginning -l is the level
+ //
+
+ SystemName = NULL;
+
+ if (argc >= 2) {
+
+ for(Index = 1; Index < argc; Index++) {
+
+ if (strncmp(argv[Index], "\\\\", 2) == 0) {
+
+ //
+ // Looks like an attempt to specify a server name.
+ // Construct a Unicode String containing the specified name
+ //
+
+ RtlInitString(&ServerNameAnsi, argv[Index]);
+ Status = RtlAnsiStringToUnicodeString(
+ &SystemNameU,
+ &ServerNameAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ printf(
+ "Failure 0x%lx to convert Server Name to Unicode\n",
+ Status
+ );
+ printf("Test abandoned\n");
+ return;
+ }
+
+ SystemName = &SystemNameU;
+
+ } else {
+
+ printf(
+ "Usage: tprivs [\\ServerName]\n"
+ );
+
+ return;
+ }
+ }
+ }
+
+ printf("TPRIV - Test Beginning\n");
+
+ Status = TestInitialize();
+
+ if (NT_SUCCESS(Status)) {
+ Status = TestPrivilegeLookup();
+ }
+
+ if (NT_SUCCESS(Status)) {
+ printf("\n\nTest Succeeded\n");
+ } else {
+ printf("\n\nTest ** FAILED **\n");
+ }
+
+
+
+ printf("TPRIV - Test End\n");
+}
+
+
+NTSTATUS
+TestInitialize()
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE ConnectHandle = NULL;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+
+ //
+ // 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 prior to opening the LSA.
+ //
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ NULL,
+ 0L,
+ (HANDLE)NULL,
+ NULL);
+
+ //
+ //
+ //
+ // The InitializeObjectAttributes macro presently stores NULL for
+ // the SecurityQualityOfService field, so we must manually copy that
+ // structure for now.
+ //
+
+ ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
+
+ //
+ // Open a handle to the LSA.
+ //
+
+ Status = LsaOpenPolicy(SystemName,
+ &ObjectAttributes,
+ GENERIC_EXECUTE,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf("TPRIV: LsaOpenPolicy() failed 0x%lx\n", Status);
+ }
+
+
+
+
+ //
+ // Now set up our internal well-known privilege LUID to programmatic name
+ // mapping.
+ //
+
+ {
+ ULONG i;
+
+
+ i=0;
+
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_CREATE_TOKEN_PRIVILEGE);
+// RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_CREATE_TOKEN_NAME) );
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, L"SeCreateTokenPrivilege" );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_ASSIGNPRIMARYTOKEN_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_LOCK_MEMORY_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_LOCK_MEMORY_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_INCREASE_QUOTA_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_INCREASE_QUOTA_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_MACHINE_ACCOUNT_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_MACHINE_ACCOUNT_NAME) );
+ i++;
+
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_TCB_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_TCB_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_SECURITY_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_SECURITY_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_TAKE_OWNERSHIP_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_TAKE_OWNERSHIP_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_LOAD_DRIVER_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_LOAD_DRIVER_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_SYSTEM_PROFILE_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_SYSTEM_PROFILE_NAME) );
+ i++;
+
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_SYSTEMTIME_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_SYSTEMTIME_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_PROF_SINGLE_PROCESS_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_PROF_SINGLE_PROCESS_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_INC_BASE_PRIORITY_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_INC_BASE_PRIORITY_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_CREATE_PAGEFILE_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_CREATE_PAGEFILE_NAME) );
+ i++;
+
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_CREATE_PERMANENT_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_CREATE_PERMANENT_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_BACKUP_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_BACKUP_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_RESTORE_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_RESTORE_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_SHUTDOWN_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_DEBUG_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_DEBUG_NAME) );
+ i++;
+
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_AUDIT_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_AUDIT_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_SYSTEM_ENVIRONMENT_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_SYSTEM_ENVIRONMENT_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_CHANGE_NOTIFY_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_CHANGE_NOTIFY_NAME) );
+ i++;
+ KnownPrivilege[i].Luid = RtlConvertLongToLargeInteger(SE_REMOTE_SHUTDOWN_PRIVILEGE);
+ RtlInitUnicodeString( &KnownPrivilege[i].ProgrammaticName, (SE_REMOTE_SHUTDOWN_NAME) );
+ i++;
+
+
+ KnownPrivilegeCount = i;
+
+ ASSERT( i == (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE +1));
+ }
+
+
+
+ return(Status);
+}
+
+
+NTSTATUS
+TestPrivilegeLookup()
+
+{
+
+ NTSTATUS Status;
+
+
+ printf("\n\n");
+
+
+
+
+ printf(" Lookup Local Representation Values . . . . . . . .Suite\n");
+ Status = TestLookupValue();
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ printf("\n Lookup Programmatic Privilege Names . . . . . . . .Suite\n");
+ Status = TestLookupProgramName();
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ printf("\n Lookup Displayable Names . . . . . . . . . . . . .Suite\n");
+ Status = TestLookupDisplayName();
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+
+ return(Status);
+
+
+
+}
+
+
+NTSTATUS
+TestLookupValue()
+
+{
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+ NTSTATUS Status;
+ ULONG i;
+ LUID Luid;
+
+
+ for (i=0; i<KnownPrivilegeCount; i++) {
+
+ printf(" %-32wZ => ", &KnownPrivilege[i].ProgrammaticName);
+ Status = LsaLookupPrivilegeValue(
+ PolicyHandle,
+ &KnownPrivilege[i].ProgrammaticName,
+ &Luid
+ );
+ if (!NT_SUCCESS(Status)) {
+ printf("** FAILED **\n");
+ printf(" Call Status is 0x%lx\n", Status);
+ CompletionStatus = Status;
+ } else {
+ if ( !EQUAL_LUID(&Luid,&KnownPrivilege[i].Luid) ) {
+ printf("** FAILED **\n");
+ printf(" LUID value not expected.\n");
+ printf(" Expected:");
+ printfLuid( (&KnownPrivilege[i].Luid) );
+ printf("\n Received:");
+ printfLuid( (&Luid) );
+ CompletionStatus = STATUS_UNSUCCESSFUL;
+ } else {
+ printfLuid( (&Luid) );
+ printf(" Succeeded\n");
+ }
+ }
+ }
+
+ return(CompletionStatus);
+}
+
+
+
+NTSTATUS
+TestLookupProgramName()
+
+{
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+ NTSTATUS Status;
+ ULONG i;
+ PUNICODE_STRING Name;
+ BOOLEAN StringsEqual;
+
+
+ for (i=0; i<KnownPrivilegeCount; i++) {
+
+ printf(" ");
+ printfLuid( (&KnownPrivilege[i].Luid) );
+ printf(" => ");
+ Status = LsaLookupPrivilegeName(
+ PolicyHandle,
+ &KnownPrivilege[i].Luid,
+ &Name
+ );
+ if (!NT_SUCCESS(Status)) {
+ printf("** FAILED **\n");
+ printf(" Status is 0x%lx\n", Status);
+ CompletionStatus = Status;
+ } else {
+ StringsEqual = RtlEqualUnicodeString(
+ Name,
+ &KnownPrivilege[i].ProgrammaticName,
+ TRUE
+ );
+ if( StringsEqual == FALSE ) {
+ printf("** FAILED **\n");
+ printf(" Program Name not expected.\n"
+ " Expected: *%wZ*\n", &KnownPrivilege[i].ProgrammaticName);
+ printf(" Received: *%wZ*", Name);
+ CompletionStatus = STATUS_UNSUCCESSFUL;
+ } else {
+ printf("%-36wZ Succeeded\n", Name);
+ }
+ MIDL_user_free( Name );
+ }
+ }
+ return(CompletionStatus);
+}
+
+
+
+NTSTATUS
+TestLookupDisplayName()
+{
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+ NTSTATUS Status;
+ ULONG i, j;
+ PUNICODE_STRING Name;
+ SHORT LanguageReturned;
+ SHORT OriginalLanguage;
+ UNICODE_STRING LanguageName;
+
+ OriginalLanguage = (USHORT)NtCurrentTeb()->CurrentLocale;
+
+ j=0;
+ while (Language[j].Id != 0) {
+ RtlInitUnicodeString( &LanguageName, Language[j].Name );
+ printf(" %wZ\n", &LanguageName);
+
+ for (i=0; i<KnownPrivilegeCount; i++) {
+
+ printf(" %-32wZ => ", &KnownPrivilege[i].ProgrammaticName);
+
+ NtCurrentTeb()->CurrentLocale = Language[j].Id;
+ Status = LsaLookupPrivilegeDisplayName(
+ PolicyHandle,
+ &KnownPrivilege[i].ProgrammaticName,
+ &Name,
+ &LanguageReturned
+ );
+ NtCurrentTeb()->CurrentLocale = OriginalLanguage;
+ if (!NT_SUCCESS(Status)) {
+ printf("** FAILED **\n");
+ printf(" Status is 0x%lx\n", Status);
+ CompletionStatus = Status;
+ } else {
+ printf(" %-45wZ\n", Name);
+ MIDL_user_free( Name );
+ }
+ }
+ printf("\n");
+ j++;
+ }
+ return(CompletionStatus);
+}
diff --git a/private/lsa/msv1_0/makefile b/private/lsa/msv1_0/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/msv1_0/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/msv1_0/msp.h b/private/lsa/msv1_0/msp.h
new file mode 100644
index 000000000..a96da3366
--- /dev/null
+++ b/private/lsa/msv1_0/msp.h
@@ -0,0 +1,334 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ msp.h
+
+Abstract:
+
+ MSV1_0 authentication package private definitions.
+
+
+
+
+Author:
+
+ Jim Kelly 11-Apr-1991
+
+Revision History:
+
+--*/
+
+#ifndef _MSP_
+#define _MSP_
+
+#if ( _MSC_VER >= 800 )
+#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
+#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
+#endif
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <crypt.h>
+#include <ntmsv1_0.h>
+#include <ntdbg.h>
+#include <stdlib.h>
+#include <string.h>
+#include "aup.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Miscellaneous macros //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// RELOCATE_ONE - Relocate a single pointer in a client buffer.
+//
+// Note: this macro is dependent on parameter names as indicated in the
+// description below. On error, this macro goes to 'Cleanup' with
+// 'Status' set to the NT Status code.
+//
+// The MaximumLength is forced to be Length.
+//
+// Define a macro to relocate a pointer in the buffer the client passed in
+// to be relative to 'ProtocolSubmitBuffer' rather than being relative to
+// 'ClientBufferBase'. The result is checked to ensure the pointer and
+// the data pointed to is within the first 'SubmitBufferSize' of the
+// 'ProtocolSubmitBuffer'.
+//
+// The relocated field must be aligned to a WCHAR boundary.
+//
+// _q - Address of UNICODE_STRING structure which points to data to be
+// relocated
+//
+
+#define RELOCATE_ONE( _q ) \
+ { \
+ ULONG Offset; \
+ \
+ Offset = ((PUCHAR)((_q)->Buffer)) - ((PUCHAR)ClientBufferBase); \
+ if ( Offset >= SubmitBufferSize || \
+ Offset + (_q)->Length > SubmitBufferSize || \
+ !COUNT_IS_ALIGNED( Offset, ALIGN_WCHAR) ) { \
+ \
+ Status = STATUS_INVALID_PARAMETER; \
+ goto Cleanup; \
+ } \
+ \
+ (_q)->Buffer = (PWSTR)(((PUCHAR)ProtocolSubmitBuffer) + Offset); \
+ (_q)->MaximumLength = (_q)->Length ; \
+ }
+
+//
+// NULL_RELOCATE_ONE - Relocate a single (possibly NULL) pointer in a client
+// buffer.
+//
+// This macro special cases a NULL pointer then calls RELOCATE_ONE. Hence
+// it has all the restrictions of RELOCATE_ONE.
+//
+//
+// _q - Address of UNICODE_STRING structure which points to data to be
+// relocated
+//
+
+#define NULL_RELOCATE_ONE( _q ) \
+ { \
+ if ( (_q)->Buffer == NULL ) { \
+ if ( (_q)->Length != 0 ) { \
+ Status = STATUS_INVALID_PARAMETER; \
+ goto Cleanup; \
+ } \
+ } else if ( (_q)->Length == 0 ) { \
+ (_q)->Buffer = NULL; \
+ } else { \
+ RELOCATE_ONE( _q ); \
+ } \
+ }
+
+
+//
+// RELOCATE_ONE_ENCODED - Relocate a unicode string pointer in a client
+// buffer. The upper byte of the length field may be an encryption seed
+// and should not be used for error checking.
+//
+// Note: this macro is dependent on parameter names as indicated in the
+// description below. On error, this macro goes to 'Cleanup' with
+// 'Status' set to the NT Status code.
+//
+// The MaximumLength is forced to be Length & 0x00ff.
+//
+// Define a macro to relocate a pointer in the buffer the client passed in
+// to be relative to 'ProtocolSubmitBuffer' rather than being relative to
+// 'ClientBufferBase'. The result is checked to ensure the pointer and
+// the data pointed to is within the first 'SubmitBufferSize' of the
+// 'ProtocolSubmitBuffer'.
+//
+// The relocated field must be aligned to a WCHAR boundary.
+//
+// _q - Address of UNICODE_STRING structure which points to data to be
+// relocated
+//
+
+#define RELOCATE_ONE_ENCODED( _q ) \
+ { \
+ ULONG Offset; \
+ \
+ Offset = ((PUCHAR)((_q)->Buffer)) - ((PUCHAR)ClientBufferBase); \
+ if ( Offset >= SubmitBufferSize || \
+ Offset + ((_q)->Length & 0x00ff) > SubmitBufferSize || \
+ !COUNT_IS_ALIGNED( Offset, ALIGN_WCHAR) ) { \
+ \
+ Status = STATUS_INVALID_PARAMETER; \
+ goto Cleanup; \
+ } \
+ \
+ (_q)->Buffer = (PWSTR)(((PUCHAR)ProtocolSubmitBuffer) + Offset); \
+ (_q)->MaximumLength = (_q)->Length & 0x00ff; \
+ }
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Authentication package dispatch routine definitions //
+// //
+///////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsaApInitializePackage(
+ IN ULONG AuthenticationPackageId,
+ IN PLSA_DISPATCH_TABLE LsaDispatchTable,
+ IN PSTRING Database OPTIONAL,
+ IN PSTRING Confidentiality OPTIONAL,
+ OUT PSTRING *AuthenticationPackageName
+ );
+
+NTSTATUS
+LsaApLogonUser(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PVOID AuthenticationInformation,
+ IN PVOID ClientAuthenticationBase,
+ IN ULONG AuthenticationInformationLength,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ OUT PLUID LogonId,
+ OUT PNTSTATUS SubStatus,
+ OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ OUT PVOID *TokenInformation,
+ OUT PUNICODE_STRING *AccountName,
+ OUT PUNICODE_STRING *AuthenticatingAuthority
+ );
+
+NTSTATUS
+LsaApCallPackage(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+VOID
+LsaApLogonTerminated(
+ IN PLUID LogonId
+ );
+
+VOID
+LsaApMsInitialize (
+ IN PLSAP_PRIVATE_LSA_SERVICES PrivateLsaApi
+ );
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// LsaApCallPackage function dispatch routines //
+// //
+///////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+MspLm20Challenge(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+NTSTATUS
+MspLm20GetChallengeResponse(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+NTSTATUS
+MspLm20EnumUsers(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+NTSTATUS
+MspLm20GetUserInfo(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+NTSTATUS
+MspLm20ReLogonUsers(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+NTSTATUS
+MspLm20ChangePassword(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// NETLOGON routines visible to main msv1_0 code //
+// //
+///////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+NlInitialize(
+ VOID
+ );
+
+NTSTATUS
+MspLm20LogonUser (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PVOID AuthenticationInformation,
+ IN PVOID ClientAuthenticationBase,
+ IN ULONG AuthenticationInformationSize,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ OUT PLUID LogonId,
+ OUT PNTSTATUS SubStatus,
+ OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ OUT PVOID *TokenInformation
+ );
+
+VOID
+MsvLm20LogonTerminated (
+ IN PLUID LogonId
+ );
+
+
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Global variables //
+// //
+///////////////////////////////////////////////////////////////////////
+
+//
+// Variables defined in msvars.c
+//
+
+extern PVOID MspHeap;
+extern ULONG MspAuthenticationPackageId;
+extern LSA_DISPATCH_TABLE Lsa;
+extern LSAP_PRIVATE_LSA_SERVICES Lsap;
+
+
+
+
+#endif // _MSP_
diff --git a/private/lsa/msv1_0/msv1_0.c b/private/lsa/msv1_0/msv1_0.c
new file mode 100644
index 000000000..6828374e9
--- /dev/null
+++ b/private/lsa/msv1_0/msv1_0.c
@@ -0,0 +1,456 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ msv1_0.c
+
+Abstract:
+
+ MSV1_0 authentication package.
+
+
+ The name of this authentication package is:
+
+
+Author:
+
+ Jim Kelly 11-Apr-1991
+
+Revision History:
+
+--*/
+
+#include "msp.h"
+
+
+
+//
+// LsaApCallPackage() function dispatch table
+//
+
+
+PLSA_AP_CALL_PACKAGE
+MspCallPackageDispatch[] = {
+ MspLm20Challenge,
+ MspLm20GetChallengeResponse,
+ MspLm20EnumUsers,
+ MspLm20GetUserInfo,
+ MspLm20ReLogonUsers,
+ MspLm20ChangePassword,
+ MspLm20ChangePassword
+ };
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Authentication package dispatch routines. //
+// //
+///////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsaApInitializePackage (
+ IN ULONG AuthenticationPackageId,
+ IN PLSA_DISPATCH_TABLE LsaDispatchTable,
+ IN PSTRING Database OPTIONAL,
+ IN PSTRING Confidentiality OPTIONAL,
+ OUT PSTRING *AuthenticationPackageName
+ )
+
+/*++
+
+Routine Description:
+
+ This service is called once by the LSA during system initialization to
+ provide the DLL a chance to initialize itself.
+
+Arguments:
+
+ AuthenticationPackageId - The ID assigned to the authentication
+ package.
+
+ LsaDispatchTable - Provides the address of a table of LSA
+ services available to authentication packages. The services
+ of this table are ordered according to the enumerated type
+ LSA_DISPATCH_TABLE_API.
+
+ Database - This parameter is not used by this authentication package.
+
+ Confidentiality - This parameter is not used by this authentication
+ package.
+
+ AuthenticationPackageName - Recieves the name of the
+ authentication package. The authentication package is
+ responsible for allocating the buffer that the string is in
+ (using the AllocateLsaHeap() service) and returning its
+ address here. The buffer will be deallocated by LSA when it
+ is no longer needed.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+
+ PSTRING NameString;
+ PCHAR NameBuffer;
+ NTSTATUS Status;
+
+
+
+ //
+ // Use the process heap for memory allocations.
+ //
+
+ MspHeap = RtlProcessHeap();
+
+ //
+ // Save our assigned authentication package ID.
+ //
+
+ MspAuthenticationPackageId = AuthenticationPackageId;
+
+
+ //
+ // Copy the LSA service dispatch table
+ //
+
+ Lsa.CreateLogonSession = LsaDispatchTable->CreateLogonSession;
+ Lsa.DeleteLogonSession = LsaDispatchTable->DeleteLogonSession;
+ Lsa.AddCredential = LsaDispatchTable->AddCredential;
+ Lsa.GetCredentials = LsaDispatchTable->GetCredentials;
+ Lsa.DeleteCredential = LsaDispatchTable->DeleteCredential;
+ Lsa.AllocateLsaHeap = LsaDispatchTable->AllocateLsaHeap;
+ Lsa.FreeLsaHeap = LsaDispatchTable->FreeLsaHeap;
+ Lsa.AllocateClientBuffer = LsaDispatchTable->AllocateClientBuffer;
+ Lsa.FreeClientBuffer = LsaDispatchTable->FreeClientBuffer;
+ Lsa.CopyToClientBuffer = LsaDispatchTable->CopyToClientBuffer;
+ Lsa.CopyFromClientBuffer = LsaDispatchTable->CopyFromClientBuffer;
+
+
+
+
+
+ //
+ // Initialize netlogon
+ //
+
+ Status = NlInitialize();
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+
+
+ //
+ // Allocate and return our package name
+ //
+
+ NameBuffer = (*Lsa.AllocateLsaHeap)(sizeof(MSV1_0_PACKAGE_NAME));
+ strcpy( NameBuffer, MSV1_0_PACKAGE_NAME);
+
+ NameString = (*Lsa.AllocateLsaHeap)( (ULONG)sizeof(STRING) );
+ RtlInitString( NameString, NameBuffer );
+ (*AuthenticationPackageName) = NameString;
+
+
+
+ return STATUS_SUCCESS;
+
+ //
+ // Appease the compiler gods by referencing all arguments
+ //
+
+ UNREFERENCED_PARAMETER(Confidentiality);
+ UNREFERENCED_PARAMETER(Database);
+
+}
+
+
+NTSTATUS
+LsaApCallPackage (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferLength,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferLength,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for
+ LsaCallAuthenticationPackage().
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ ProtocolSubmitBuffer - Supplies a protocol message specific to
+ the authentication package.
+
+ ClientBufferBase - Provides the address within the client
+ process at which the protocol message was resident.
+ This may be necessary to fix-up any pointers within the
+ protocol message buffer.
+
+ SubmitBufferLength - Indicates the length of the submitted
+ protocol message buffer.
+
+ ProtocolReturnBuffer - Is used to return the address of the
+ protocol buffer in the client process. The authentication
+ package is responsible for allocating and returning the
+ protocol buffer within the client process. This buffer is
+ expected to have been allocated with the
+ AllocateClientBuffer() service.
+
+ The format and semantics of this buffer are specific to the
+ authentication package.
+
+ ReturnBufferLength - Receives the length (in bytes) of the
+ returned protocol buffer.
+
+ ProtocolStatus - Assuming the services completion is
+ STATUS_SUCCESS, this parameter will receive completion status
+ returned by the specified authentication package. The list
+ of status values that may be returned are authentication
+ package specific.
+
+Return Status:
+
+ STATUS_SUCCESS - The call was made to the authentication package.
+ The ProtocolStatus parameter must be checked to see what the
+ completion status from the authentication package is.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the return
+ buffer could not could not be allocated because the client
+ does not have sufficient quota.
+
+
+
+
+--*/
+
+{
+ ULONG MessageType;
+
+ //
+ // Get the messsage type from the protocol submit buffer.
+ //
+
+ if ( SubmitBufferLength < sizeof(MSV1_0_PROTOCOL_MESSAGE_TYPE) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ MessageType =
+ (ULONG) *((PMSV1_0_PROTOCOL_MESSAGE_TYPE)(ProtocolSubmitBuffer));
+
+ if ( MessageType >=
+ (sizeof(MspCallPackageDispatch)/sizeof(MspCallPackageDispatch[0])) ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Allow the dispatch routines to only set the return buffer information
+ // on success conditions.
+ //
+
+ *ProtocolReturnBuffer = NULL;
+ *ReturnBufferLength = 0;
+
+ //
+ // Call the appropriate routine for this message.
+ //
+
+ return (*(MspCallPackageDispatch[MessageType]))(
+ ClientRequest,
+ ProtocolSubmitBuffer,
+ ClientBufferBase,
+ SubmitBufferLength,
+ ProtocolReturnBuffer,
+ ReturnBufferLength,
+ ProtocolStatus ) ;
+
+}
+
+
+NTSTATUS
+LsaApCallPackageUntrusted (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferLength,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferLength,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for
+ LsaCallAuthenticationPackage() for untrusted clients.
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ ProtocolSubmitBuffer - Supplies a protocol message specific to
+ the authentication package.
+
+ ClientBufferBase - Provides the address within the client
+ process at which the protocol message was resident.
+ This may be necessary to fix-up any pointers within the
+ protocol message buffer.
+
+ SubmitBufferLength - Indicates the length of the submitted
+ protocol message buffer.
+
+ ProtocolReturnBuffer - Is used to return the address of the
+ protocol buffer in the client process. The authentication
+ package is responsible for allocating and returning the
+ protocol buffer within the client process. This buffer is
+ expected to have been allocated with the
+ AllocateClientBuffer() service.
+
+ The format and semantics of this buffer are specific to the
+ authentication package.
+
+ ReturnBufferLength - Receives the length (in bytes) of the
+ returned protocol buffer.
+
+ ProtocolStatus - Assuming the services completion is
+ STATUS_SUCCESS, this parameter will receive completion status
+ returned by the specified authentication package. The list
+ of status values that may be returned are authentication
+ package specific.
+
+Return Status:
+
+ STATUS_SUCCESS - The call was made to the authentication package.
+ The ProtocolStatus parameter must be checked to see what the
+ completion status from the authentication package is.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the return
+ buffer could not could not be allocated because the client
+ does not have sufficient quota.
+
+
+
+
+--*/
+
+{
+ ULONG MessageType;
+
+ //
+ // Get the messsage type from the protocol submit buffer.
+ //
+
+ if ( SubmitBufferLength < sizeof(MSV1_0_PROTOCOL_MESSAGE_TYPE) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ MessageType =
+ (ULONG) *((PMSV1_0_PROTOCOL_MESSAGE_TYPE)(ProtocolSubmitBuffer));
+
+ if ( MessageType >=
+ (sizeof(MspCallPackageDispatch)/sizeof(MspCallPackageDispatch[0])) ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Untrusted clients are only allowed to call the ChangePassword function.
+ //
+
+ if ((MSV1_0_PROTOCOL_MESSAGE_TYPE) MessageType != MsV1_0ChangePassword) {
+
+ return STATUS_ACCESS_DENIED;
+ }
+
+ //
+ // Allow the dispatch routines to only set the return buffer information
+ // on success conditions.
+ //
+
+ *ProtocolReturnBuffer = NULL;
+ *ReturnBufferLength = 0;
+
+ //
+ // Call the appropriate routine for this message.
+ //
+
+ return (*(MspCallPackageDispatch[MessageType]))(
+ ClientRequest,
+ ProtocolSubmitBuffer,
+ ClientBufferBase,
+ SubmitBufferLength,
+ ProtocolReturnBuffer,
+ ReturnBufferLength,
+ ProtocolStatus ) ;
+
+}
+
+
+
+VOID
+LsaApMsInitialize (
+ IN PLSAP_PRIVATE_LSA_SERVICES PrivateLsaApi
+ )
+
+/*++
+
+Routine Description:
+
+ This initialization routine is called by the LSA before normal
+ package initialization to pass a table of private LSA routine addresses.
+ This is intended for use by the standard Microsoft authentication packages.
+ only.
+
+
+
+Arguments:
+
+ PrivateLsaApi - Provides the address of a table of private LSA
+ services available to Microsoft authentication packages. The services
+ of this table are ordered according to the enumerated type
+ LSA_PRIVATE_LSA_SERVICES.
+
+
+Return Status:
+
+ None.
+
+
+
+--*/
+
+{
+
+ //
+ // Copy the private LSA service dispatch table
+ //
+
+ Lsap.GetOperationalMode = PrivateLsaApi->GetOperationalMode;
+ Lsap.ImpersonateClient = PrivateLsaApi->ImpersonateClient;
+
+
+ return;
+
+}
diff --git a/private/lsa/msv1_0/msv1_0.def b/private/lsa/msv1_0/msv1_0.def
new file mode 100644
index 000000000..e97b1e8a3
--- /dev/null
+++ b/private/lsa/msv1_0/msv1_0.def
@@ -0,0 +1,15 @@
+LIBRARY msv1_0
+
+DESCRIPTION 'Microsoft Authentication Package V1_0'
+
+EXPORTS
+ LsaApInitializePackage
+ LsaApLogonUserEx
+ LsaApCallPackage
+ LsaApLogonTerminated
+ LsaApMsInitialize
+ MsvSamLogoff
+ MsvSamValidate
+ MsvGetLogonAttemptCount
+ LsaApCallPackageUntrusted
+
diff --git a/private/lsa/msv1_0/msv1_0.prf b/private/lsa/msv1_0/msv1_0.prf
new file mode 100644
index 000000000..4cc9bd4be
--- /dev/null
+++ b/private/lsa/msv1_0/msv1_0.prf
@@ -0,0 +1,173 @@
+LsaApCallPackage@28
+NlpPutClientString@12
+NlpFindActiveLogon@8
+MspLm20GetUserInfo@28
+MspLm20GetChallengeResponse@28
+NlpPutString@12
+NlpMakeDomainRelativeSid@8
+LsaApLogonUser@52
+NlpMakeTokenInformationV1@8
+NlpAllocateNetworkProfile@16
+NlpMakePrimaryCredential@20
+NlpGetPrimaryCredential@12
+MsvpPasswordValidate@28
+MsvpSamValidate@52
+MsvSamValidate@52
+LsaApLogonTerminated@4
+NlWaitForEvent@8
+NlWaitForNetlogon@4
+NlSamInitialize@4
+NlpMakeRelativeString@8
+NlpRelativeToAbsolute@8
+NlpCountActiveLogon@8
+NlpAllocateInteractiveProfile@16
+NlpAddPrimaryCredential@12
+LsaApInitializePackage@20
+LsaApMsInitialize@4
+NlInitialize@0
+NlpLoadNetlogonDll@0
+MspLm20EnumUsers@28
+MspDisableAdminsAlias@0
+MspAddBackslashesComputerName@8
+MspChangePasswordSam@32
+MspChangePasswordDownlevel@16
+MspChangePassword@32
+MspLm20ChangePassword@28
+MsvSamLogoff@12
+MspLm20Challenge@28
+MspLm20ReLogonUsers@28
+NlpLoadNetapiDll@0
+NlpDeletePrimaryCredential@4
+NlpChangePassword@16
+
+NlpCacheInitialize@0
+NlpCacheTerminate@0
+NlpAddCacheEntry@8
+NlpGetCacheEntry@12
+NlpDeleteCacheEntry@4
+NlpChangeCachePassword@16
+
+NlpInternalCacheInitialize@0
+NlpGetCacheControlInfo@0
+NlpBuildCteTable@0
+NlpChangeCacheSizeIfNecessary@0
+NlpWriteCacheControl@0
+NlpMakeCacheEntryName@8
+NlpMakeNewCacheEntry@4
+NlpEliminateCacheEntry@4
+NlpConvert1_0To1_0A@0
+NlpOpen_Nt1_0_Secret@0
+NlpReadCacheEntryByIndex@16
+NlpAddEntryToActiveList@4
+NlpAddEntryToInactiveList@4
+NlpGetFreeEntryIndex@4
+NlpBuildCacheEntry@16
+NlpOpenCache@0
+NlpCloseCache@0
+NlpOpenSecret@4
+NlpCloseSecret@0
+NlpWriteSecret@8
+NlpReadSecret@8
+NlpMakeSecretPassword@12
+NlpReadCacheEntry@24
+NlpWriteCacheEntry@12
+NlpCopyAndUpdateAccountInfo@16
+NlpSetTimeField@8
+NlpBuildAccountInfo@12
+
+CloseServiceHandle@4
+DbgPrint
+GetComputerNameW@8
+GetLastError@0
+GetProcAddress@8
+LoadLibraryA@4
+LsaClose@4
+LsaCreateSecret@16
+LsaFreeMemory@4
+LsaIFree_LSAPR_POLICY_INFORMATION@8
+LsaIOpenPolicyTrusted@4
+LsaOpenPolicy@16
+LsaOpenSecret@16
+LsaQueryInformationPolicy@12
+LsaQuerySecret@20
+LsaSetSecret@12
+LsarQueryInformationPolicy@12
+MIDL_user_allocate@4
+MIDL_user_free@4
+MIDL_user_reallocate@8
+MIDL_user_size@4
+LocalAlloc@8
+LocalFree@4
+LocalHandle@4
+LocalReAlloc@12
+LocalSize@4
+NtAdjustGroupsToken@24
+NtAllocateLocallyUniqueId@4
+NtClose@4
+NtCreateEvent@20
+NtCreateKey@28
+NtFlushKey@4
+NtOpenEvent@12
+NtOpenThreadToken@16
+NtQuerySystemTime@4
+NtQueryValueKey@24
+NtSetInformationThread@16
+NtSetValueKey@24
+NtWaitForSingleObject@12
+OpenSCManagerW@12
+OpenServiceW@12
+QueryServiceConfigW@16
+QueryServiceStatus@8
+RtlAllocateAndInitializeSid@44
+RtlAllocateHeap@12
+RtlAssert@16
+RtlCompareMemory@12
+RtlCopySid@12
+RtlCopyUnicodeString@8
+RtlDeleteCriticalSection@4
+RtlEnterCriticalSection@4
+RtlEqualDomainName@8
+RtlEqualUnicodeString@12
+RtlEraseUnicodeString@4
+RtlFreeHeap@12
+RtlFreeSid@4
+RtlGetNtProductType@4
+RtlImpersonateSelf@4
+RtlInitString@8
+RtlInitUnicodeString@8
+RtlInitializeCriticalSection@4
+RtlLeaveCriticalSection@4
+RtlLengthRequiredSid@4
+RtlLengthSid@4
+RtlRunDecodeUnicodeString@8
+RtlSubAuthorityCountSid@4
+RtlSubAuthoritySid@8
+RtlTimeToTimeFields@8
+RtlUpperChar@4
+SamChangePasswordUser@12
+SamCloseHandle@4
+SamConnect@16
+SamFreeMemory@4
+SamIAccountRestrictions@24
+SamIConnect@16
+SamIFree_SAMPR_GET_GROUPS_BUFFER@4
+SamIFree_SAMPR_ULONG_ARRAY@4
+SamIFree_SAMPR_USER_INFO_BUFFER@8
+SamLookupNamesInDomain@20
+SamOpenDomain@16
+SamOpenUser@16
+SamQueryInformationDomain@12
+SamrCloseHandle@4
+SamrGetGroupsForUser@8
+SamrLookupNamesInDomain@20
+SamrOpenDomain@16
+SamrOpenUser@16
+SamrQueryInformationUser@12
+SamrSetInformationUser@12
+Sleep@4
+SystemFunction003@8
+SystemFunction006@8
+SystemFunction007@8
+SystemFunction008@12
+SystemFunction009@12
+SystemFunction011@12
diff --git a/private/lsa/msv1_0/msv1_0.rc b/private/lsa/msv1_0/msv1_0.rc
new file mode 100644
index 000000000..a7a3dff77
--- /dev/null
+++ b/private/lsa/msv1_0/msv1_0.rc
@@ -0,0 +1,38 @@
+#include <windows.h>
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define VER_FILETYPE VFT_DLL
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR "Microsoft Authentication Package v1.0"
+#define VER_INTERNALNAME_STR "MSV1_0.DLL"
+#define VER_ORIGINALFILENAME_STR "MSV1_0.DLL"
+
+#include "common.ver"
diff --git a/private/lsa/msv1_0/msvars.c b/private/lsa/msv1_0/msvars.c
new file mode 100644
index 000000000..d84e4e52d
--- /dev/null
+++ b/private/lsa/msv1_0/msvars.c
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ msvars.c
+
+Abstract:
+
+ This module contains variables used within the msv1_0 authentication
+ package.
+
+Author:
+
+ Jim Kelly (JimK) 11-Apr-1991
+
+Environment:
+
+ User mode - msv1_0 authentication package DLL
+
+Revision History:
+
+
+--*/
+
+#include "msp.h"
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// READ ONLY Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+//
+// msv1_0 private heap.
+//
+
+PVOID MspHeap;
+
+
+//
+// package ID assigned to msv1_0 by the LSA.
+//
+
+ULONG MspAuthenticationPackageId;
+
+
+//
+// dispatch table of (public) LSA service routines.
+//
+
+LSA_DISPATCH_TABLE Lsa;
+
+
+//
+// dispatch table of (Private) LSA service routines.
+//
+
+LSAP_PRIVATE_LSA_SERVICES Lsap;
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// READ/WRITE Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+
diff --git a/private/lsa/msv1_0/msvpaswd.c b/private/lsa/msv1_0/msvpaswd.c
new file mode 100644
index 000000000..15f008f22
--- /dev/null
+++ b/private/lsa/msv1_0/msvpaswd.c
@@ -0,0 +1,1690 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ msvpaswd.c
+
+Abstract:
+
+ This file contains the MSV1_0 Authentication Package password routines.
+
+Author:
+
+ Dave Hart (davehart) 12-Mar-1992
+
+Revision History:
+
+--*/
+
+#include "msp.h"
+#include "nlp.h"
+#include <stdlib.h>
+#include <rpc.h>
+#include <samisrv.h>
+#include <lsarpc.h>
+#include <lsaisrv.h>
+#include <lmcons.h>
+#include <lmerr.h>
+#include <lmapibuf.h>
+#include <lmaccess.h>
+#include <lmremutl.h>
+#include <lmsname.h>
+#include <lmwksta.h>
+#include <winsvc.h>
+#include "nlpcache.h"
+
+
+
+NTSTATUS
+MspStopImpersonating (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Stop impersonating. This is used to stop impersonating either
+ ourselves (see MspDisableAdminsAlis) or a client.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ Other values are unexpected, but will be those returned by
+ NtSetInformationThread.
+
+--*/
+
+{
+
+ NTSTATUS
+ NtStatus;
+
+ HANDLE
+ Token = NULL;
+
+ NtStatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&Token,
+ sizeof(Token)
+ );
+
+#if DBG
+ if ( !NT_SUCCESS(NtStatus) ) {
+
+ KdPrint(("MspStopImpersonating: Cannot stop impersonating, status %x\n",
+ NtStatus));
+ }
+#endif \\DBG
+
+ return( NtStatus );
+
+}
+
+NTSTATUS
+MspDisableAdminsAlias (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Remove the current thread from the Administrators alias. This
+ is accomplished by impersonating our own thread, then removing
+ the Administrators alias membership from the impersonation
+ token. Use MspStopImpersonating() to stop impersonating and
+ thereby restore the thread to the Administrators alias.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+ HANDLE TokenHandle = NULL;
+ SID_IDENTIFIER_AUTHORITY IdentifierAuthority = SECURITY_NT_AUTHORITY;
+ PSID AdminSid = NULL;
+ TOKEN_GROUPS TokenGroups;
+
+ //
+ // Make sure we aren't impersonating anyone else
+ // (that will prevent the RtlImpersonateSelf() call from succeeding).
+ //
+
+ Status = MspStopImpersonating();
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ //
+ // Impersonate ourself so we can munge the token, then later stop
+ // impersonating and revert to the original token.
+ //
+
+ Status = RtlImpersonateSelf( SecurityDelegation );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Open the impersonation token.
+ //
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ GENERIC_READ | GENERIC_WRITE,
+ TRUE,
+ &TokenHandle
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ KdPrint(("MspDisableAdminsAlias: NtOpenThreadToken returns %x\n",
+ Status));
+
+ goto Cleanup;
+ }
+
+ //
+ // Build the SID for the Administrators alias. The Administrators
+ // alias SID is well known, S-1-5-32-544.
+ //
+
+ Status = RtlAllocateAndInitializeSid(
+ &IdentifierAuthority, // SECURITY_NT_AUTHORITY (5)
+ 2, // SubAuthorityCount
+ SECURITY_BUILTIN_DOMAIN_RID, // 32
+ DOMAIN_ALIAS_RID_ADMINS, // 544
+ 0,0,0,0,0,0,
+ &AdminSid
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ KdPrint(("MspDisableAdminsAlias: RtlAllocateAndInitializeSid returns %x\n",
+ Status));
+ goto Cleanup;
+ }
+
+ //
+ // Disable the Administrators alias.
+ //
+
+ TokenGroups.GroupCount = 1;
+ TokenGroups.Groups[0].Sid = AdminSid;
+ TokenGroups.Groups[0].Attributes = 0; // SE_GROUP_ENABLED not on.
+
+ Status = NtAdjustGroupsToken(
+ TokenHandle,
+ FALSE,
+ &TokenGroups,
+ 0,
+ NULL,
+ NULL
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ KdPrint(("MspDisableAdminsAlias: NtAdjustGroupsToken returns %x\n",
+ Status));
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if (AdminSid) {
+ RtlFreeSid(AdminSid);
+ }
+
+ if (TokenHandle) {
+ NtClose(TokenHandle);
+ }
+
+ //
+ // If we're failing, stop impersonation.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ NTSTATUS TempStatus;
+
+ TempStatus = MspStopImpersonating();
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+MspAddBackslashesComputerName(
+ IN PUNICODE_STRING ComputerName,
+ OUT PUNICODE_STRING UncComputerName
+ )
+
+/*++
+
+Routine Description:
+
+ This function makes a copy of a Computer Name, prepending backslashes
+ if they are not already present.
+
+Arguments:
+
+ ComputerName - Pointer to Computer Name without backslashes.
+
+ UncComputerName - Pointer to Unicode String structure that will be
+ initialized to reference the computerName with backslashes
+ prepended if not already present. The Unicode Buffer will be
+ terminated with a Unicode NULL, so that it can be passed as
+ a parameter to routines expecting a null terminated Wide String.
+ When this string is finished with, the caller must free its
+ memory via RtlFreeHeap.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN HasBackslashes = FALSE;
+ BOOLEAN IsNullTerminated = FALSE;
+ USHORT OutputNameLength;
+ USHORT OutputNameMaximumLength;
+ PWSTR StartBuffer = NULL;
+
+ //
+ // If the computername is NULL, a zero length string, or the name already begins with
+ // backslashes and is wide char null terminated, just use it unmodified.
+ //
+
+ if ((!ARGUMENT_PRESENT(ComputerName)) ||
+ (ComputerName->Length == (USHORT) 0)) {
+
+ *UncComputerName = *ComputerName;
+ goto AddBackslashesComputerNameFinish;
+ }
+
+ //
+ // Name is not NULL or zero length. Check if name already has
+ // backslashes and a trailing Unicode Null
+ //
+
+ OutputNameLength = ComputerName->Length + (2 * sizeof(WCHAR));
+ OutputNameMaximumLength = OutputNameLength + sizeof(WCHAR);
+
+ if ((ComputerName && ComputerName->Length >= 2 * sizeof(WCHAR)) &&
+ (ComputerName->Buffer[0] == L'\\') &&
+ (ComputerName->Buffer[1] == L'\\')) {
+
+ HasBackslashes = TRUE;
+ OutputNameLength -= (2 * sizeof(WCHAR));
+ OutputNameMaximumLength -= (2 * sizeof(WCHAR));
+ }
+
+ if ((ComputerName->Length + (USHORT) sizeof(WCHAR) <= ComputerName->MaximumLength) &&
+ (ComputerName->Buffer[ComputerName->Length/sizeof(WCHAR)] == UNICODE_NULL)) {
+
+ IsNullTerminated = TRUE;
+ }
+
+ if (HasBackslashes && IsNullTerminated) {
+
+ *UncComputerName = *ComputerName;
+ goto AddBackslashesComputerNameFinish;
+ }
+
+ //
+ // Name either does not have backslashes or is not NULL terminated.
+ // Make a copy with leading backslashes and a wide NULL terminator.
+ //
+
+ UncComputerName->Length = OutputNameLength;
+ UncComputerName->MaximumLength = OutputNameMaximumLength;
+
+ UncComputerName->Buffer = RtlAllocateHeap(
+ MspHeap,
+ 0,
+ OutputNameMaximumLength
+ );
+
+ if (UncComputerName->Buffer == NULL) {
+
+ KdPrint(("MspAddBackslashes...: Out of memory copying ComputerName.\n"));
+ Status = STATUS_NO_MEMORY;
+ goto AddBackslashesComputerNameError;
+ }
+
+ StartBuffer = UncComputerName->Buffer;
+
+ if (!HasBackslashes) {
+
+ UncComputerName->Buffer[0] = UncComputerName->Buffer[1] = L'\\';
+ StartBuffer +=2;
+ }
+
+ RtlCopyMemory(
+ StartBuffer,
+ ComputerName->Buffer,
+ ComputerName->Length
+ );
+
+ UncComputerName->Buffer[UncComputerName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+AddBackslashesComputerNameFinish:
+
+ return(Status);
+
+AddBackslashesComputerNameError:
+
+ goto AddBackslashesComputerNameFinish;
+}
+
+NTSTATUS
+MspChangePasswordSam(
+ IN PUNICODE_STRING UncComputerName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING OldPassword,
+ IN PUNICODE_STRING NewPassword,
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN Impersonating,
+ OUT PDOMAIN_PASSWORD_INFORMATION *DomainPasswordInfo,
+ OUT PPOLICY_PRIMARY_DOMAIN_INFO *PrimaryDomainInfo OPTIONAL,
+ OUT PBOOLEAN Authoritative
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by MspChangePassword to change the password
+ on a Windows NT machine.
+
+Arguments:
+
+ UncComputerName - Name of the target machine. This name must begin with
+ two backslashes.
+
+ UserName - Name of the user to change password for.
+
+ OldPassword - Plaintext current password.
+
+ NewPassword - Plaintext replacement password.
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ DomainPasswordInfo - Password restriction information (returned only if
+ status is STATUS_PASSWORD_RESTRICTION).
+
+ PrimaryDomainInfo - DomainNameInformation (returned only if status is
+ STATUS_BACKUP_CONTROLLER).
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ ...
+
+--*/
+
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQos;
+ SAM_HANDLE SamHandle = NULL;
+ SAM_HANDLE DomainHandle = NULL;
+ LSA_HANDLE LSAPolicyHandle = NULL;
+ OBJECT_ATTRIBUTES LSAObjectAttributes;
+ PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
+
+ //
+ // If we're impersonating (ie, winlogon impersonated its caller before calling us),
+ // impersonate again. This allows us to get the name of the caller for auditing.
+ //
+
+ *Authoritative = FALSE;
+
+ if ( Impersonating ) {
+
+ Status = (*Lsap.ImpersonateClient)(ClientRequest);
+
+ } else {
+
+ //
+ // Since the System context is a member of the Administrators alias,
+ // when we connect with the local SAM we come in as an Administrator.
+ // (When it's remote, we go over the null session and so have very
+ // low access). We don't want to be an Administrator because that
+ // would allow the user to change the password on an account whose
+ // ACL prohibits the user from changing the password. So we'll
+ // temporarily impersonate ourself and disable the Administrators
+ // alias in the impersonation token.
+ //
+
+ Status = MspDisableAdminsAlias();
+ }
+
+
+
+
+ if (!NT_SUCCESS( Status )) {
+ goto Cleanup;
+ }
+
+ Status = SamChangePasswordUser2(
+ UncComputerName,
+ UserName,
+ OldPassword,
+ NewPassword
+ );
+
+
+ if ( !NT_SUCCESS(Status) ) {
+ if (( Status == STATUS_WRONG_PASSWORD ) ||
+ ( Status == STATUS_PASSWORD_RESTRICTION ) ||
+ ( Status == STATUS_ACCOUNT_RESTRICTION ) ||
+ ( Status == STATUS_ILL_FORMED_PASSWORD) ) {
+ *Authoritative = TRUE;
+ } else if ( ( Status != RPC_NT_SERVER_UNAVAILABLE) &&
+ ( Status != STATUS_INVALID_DOMAIN_ROLE) &&
+ (Impersonating) ) {
+
+#ifdef COMPILED_BY_DEVELOPER
+ KdPrint(("MspChangePasswordSam: SamChangePasswordUser2(%wZ) failed, status %x\n",
+ UncComputerName, Status));
+#endif // COMPILED_BY_DEVELOPER
+
+ //
+ // If we failed to connect and we were impersonating a client
+ // then we may want to try again using the NULL session.
+ // Only try this if we found a server last try. Otherwise,
+ // we'll subject our user to another long timeout.
+ //
+
+
+ Status = MspDisableAdminsAlias();
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = SamChangePasswordUser2(
+ UncComputerName,
+ UserName,
+ OldPassword,
+ NewPassword
+ );
+
+
+ if ( !NT_SUCCESS(Status) &&
+ (( Status == STATUS_WRONG_PASSWORD ) ||
+ ( Status == STATUS_PASSWORD_RESTRICTION ) ||
+ ( Status == STATUS_ACCOUNT_RESTRICTION ) ||
+ ( Status == STATUS_ILL_FORMED_PASSWORD) )) {
+ *Authoritative = TRUE;
+ }
+ }
+ }
+
+
+
+ if ( !NT_SUCCESS(Status) ) {
+
+#ifdef COMPILED_BY_DEVELOPER
+ KdPrint(("MspChangePasswordSam: Cannot change password for %wZ, status %x\n",
+ UserName, Status));
+#endif // COMPILED_BY_DEVELOPER
+ if (Status == RPC_NT_SERVER_UNAVAILABLE ||
+ Status == RPC_S_SERVER_UNAVAILABLE ) {
+
+ Status = STATUS_CANT_ACCESS_DOMAIN_INFO;
+ } else if (Status == STATUS_PASSWORD_RESTRICTION) {
+
+ //
+ // Get the password restrictions for this domain and return them
+ //
+
+
+
+ //
+ // Get the SID of the account domain from LSA
+ //
+
+ InitializeObjectAttributes( &LSAObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ Status = LsaOpenPolicy( UncComputerName,
+ &LSAObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LSAPolicyHandle );
+
+ if( !NT_SUCCESS(Status) ) {
+ KdPrint(("MspChangePasswordSam: LsaOpenPolicy(%wZ) failed, status %x\n",
+ UncComputerName, Status));
+ LSAPolicyHandle = NULL;
+ goto Cleanup;
+ }
+
+ Status = LsaQueryInformationPolicy(
+ LSAPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) &AccountDomainInfo );
+
+ if( !NT_SUCCESS(Status) ) {
+ KdPrint(("MspChangePasswordSam: LsaQueryInformationPolicy(%wZ) failed, status %x\n",
+ UncComputerName, Status));
+ AccountDomainInfo = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Setup ObjectAttributes for SamConnect call.
+ //
+
+ 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(
+ UncComputerName,
+ &SamHandle,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ &ObjectAttributes
+ );
+
+
+ if ( !NT_SUCCESS(Status) ) {
+ KdPrint(("MspChangePasswordSam: Cannot open sam on %wZ, status %x\n",
+ UncComputerName, Status));
+ DomainHandle = NULL;
+ goto Cleanup;
+ }
+
+
+ //
+ // Open the Account domain in SAM.
+ //
+
+ Status = SamOpenDomain(
+ SamHandle,
+ GENERIC_EXECUTE,
+ AccountDomainInfo->DomainSid,
+ &DomainHandle
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ KdPrint(("MspChangePasswordSam: Cannot open domain on %wZ, status %x\n",
+ UncComputerName, Status));
+ DomainHandle = NULL;
+ goto Cleanup;
+ }
+
+
+ Status = SamQueryInformationDomain(
+ DomainHandle,
+ DomainPasswordInformation,
+ (PVOID *)DomainPasswordInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ *DomainPasswordInfo = NULL;
+ } else {
+ Status = STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+ goto Cleanup;
+ }
+
+
+Cleanup:
+
+ //
+ // If the only problem is that this is a BDC,
+ // Return the domain name back to the caller.
+ //
+
+ if ( (Status == STATUS_BACKUP_CONTROLLER ||
+ Status == STATUS_INVALID_DOMAIN_ROLE) &&
+ PrimaryDomainInfo != NULL ) {
+
+ NTSTATUS TempStatus;
+
+ //
+ // Open the LSA if we haven't already.
+ //
+
+ if (LSAPolicyHandle == NULL) {
+
+ InitializeObjectAttributes( &LSAObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ TempStatus = LsaOpenPolicy( UncComputerName,
+ &LSAObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &LSAPolicyHandle );
+
+ if( !NT_SUCCESS(TempStatus) ) {
+ KdPrint(("MspChangePasswordSam: LsaOpenPolicy(%wZ) failed, status %x\n",
+ UncComputerName, TempStatus));
+ LSAPolicyHandle = NULL;
+ }
+
+
+ }
+
+ if (LSAPolicyHandle != NULL) {
+ TempStatus = LsaQueryInformationPolicy(
+ LSAPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PVOID *) PrimaryDomainInfo );
+
+ if( !NT_SUCCESS(TempStatus) ) {
+ KdPrint(("MspChangePasswordSam: LsaQueryInformationPolicy(%wZ) failed, status %x\n",
+ UncComputerName, TempStatus));
+ *PrimaryDomainInfo = NULL;
+ #ifdef COMPILED_BY_DEVELOPER
+ } else {
+ KdPrint(("MspChangePasswordSam: %wZ is really a BDC in domain %wZ\n",
+ UncComputerName, &(*PrimaryDomainInfo)->Name));
+ #endif // COMPILED_BY_DEVELOPER
+ }
+ }
+
+ Status = STATUS_BACKUP_CONTROLLER;
+
+ }
+
+ //
+ // Stop impersonating.
+ //
+
+ {
+ NTSTATUS TempStatus;
+
+ TempStatus = MspStopImpersonating();
+
+ }
+
+ //
+ // Free Locally used resources
+ //
+
+
+ if (SamHandle) {
+ SamCloseHandle(SamHandle);
+ }
+
+ if (DomainHandle) {
+ SamCloseHandle(DomainHandle);
+ }
+
+ if( LSAPolicyHandle != NULL ) {
+ LsaClose( LSAPolicyHandle );
+ }
+
+ if ( AccountDomainInfo != NULL ) {
+ (VOID) LsaFreeMemory( AccountDomainInfo );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+MspChangePasswordDownlevel(
+ IN PUNICODE_STRING UncComputerName,
+ IN PUNICODE_STRING UserNameU,
+ IN PUNICODE_STRING OldPasswordU,
+ IN PUNICODE_STRING NewPasswordU,
+ OUT PBOOLEAN Authoritative
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by MspChangePassword to change the password
+ on an OS/2 User-level server. First we try sending an encrypted
+ request to the server, failing that we fall back on plaintext.
+
+Arguments:
+
+ UncComputerName - Pointer to Unicode String containing the Name of the
+ target machine. This name must begin with two backslashes and
+ must be null terminated.
+
+ UserNameU - Name of the user to change password for.
+
+ OldPasswordU - Plaintext current password.
+
+ NewPasswordU - Plaintext replacement password.
+
+ Authoritative - If the attempt failed with an error that would
+ otherwise cause the password attempt to fail, this flag, if false,
+ indicates that the error was not authoritative and the attempt
+ should proceed.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+ DWORD Length;
+ LPWSTR UserName = NULL;
+ LPWSTR OldPassword = NULL;
+ LPWSTR NewPassword = NULL;
+
+ *Authoritative = TRUE;
+
+ //
+ // Convert UserName from UNICODE_STRING to null-terminated wide string
+ // for use by RxNetUserPasswordSet.
+ //
+
+ Length = UserNameU->Length;
+
+ UserName = RtlAllocateHeap(
+ MspHeap, 0,
+ Length + sizeof(TCHAR)
+ );
+
+ if ( NULL == UserName ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( UserName, UserNameU->Buffer, Length );
+
+ UserName[ Length / sizeof(TCHAR) ] = 0;
+
+ //
+ // Convert OldPassword from UNICODE_STRING to null-terminated wide string.
+ //
+
+ Length = OldPasswordU->Length;
+
+ OldPassword = RtlAllocateHeap( MspHeap, 0, Length + sizeof(TCHAR) );
+
+ if ( NULL == OldPassword ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( OldPassword, OldPasswordU->Buffer, Length );
+
+ OldPassword[ Length / sizeof(TCHAR) ] = 0;
+
+ //
+ // Convert NewPassword from UNICODE_STRING to null-terminated wide string.
+ //
+
+ Length = NewPasswordU->Length;
+
+ NewPassword = RtlAllocateHeap( MspHeap, 0, Length + sizeof(TCHAR) );
+
+ if ( NULL == NewPassword ) {
+
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( NewPassword, NewPasswordU->Buffer, Length );
+
+ NewPassword[ Length / sizeof(TCHAR) ] = 0;
+
+#ifdef COMPILED_BY_DEVELOPER
+
+ KdPrint(("MSV1_0: Changing password on downlevel server:\n"
+ "\tUncComputerName: %wZ\n"
+ "\tUserName: %ws\n"
+ "\tOldPassword: %ws\n"
+ "\tNewPassword: %ws\n",
+ UncComputerName,
+ UserName,
+ OldPassword,
+ NewPassword
+ ));
+
+#endif // COMPILED_BY_DEVELOPER
+
+ //
+ // Attempt to change password on downlevel server.
+ //
+
+ NetStatus = (*NlpRxNetUserPasswordSet)(
+ UncComputerName->Buffer,
+ UserName,
+ OldPassword,
+ NewPassword);
+
+#ifdef COMPILED_BY_DEVELOPER
+ KdPrint(("MSV1_0: RxNUserPasswordSet returns %d.\n", NetStatus));
+#endif // COMPILED_BY_DEVELOPER
+
+ // Since we overload the computername as the domain name,
+ // map NERR_InvalidComputer to STATUS_NO_SUCH_DOMAIN, since
+ // that will give the user a nice error message.
+ //
+ // ERROR_PATH_NOT_FOUND is returned on a standalone workstation that
+ // doesn't have the network installed.
+ //
+
+ if (NetStatus == NERR_InvalidComputer ||
+ NetStatus == ERROR_PATH_NOT_FOUND) {
+
+ Status = STATUS_NO_SUCH_DOMAIN;
+ *Authoritative = FALSE;
+
+ // ERROR_SEM_TIMEOUT can be returned when the computer name doesn't
+ // exist.
+ //
+ // ERROR_REM_NOT_LIST can also be returned when the computer name
+ // doesn't exist.
+ //
+
+ } else if ( NetStatus == ERROR_SEM_TIMEOUT ||
+ NetStatus == ERROR_REM_NOT_LIST) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ *Authoritative = FALSE;
+
+ } else if ( (NetStatus == ERROR_INVALID_PARAMETER) &&
+ ((wcslen(NewPassword) > LM20_PWLEN) ||
+ (wcslen(OldPassword) > LM20_PWLEN)) ) {
+
+ //
+ // The net api returns ERROR_INVALID_PARAMETER if the password
+ // could not be converted to the LM OWF password. Return
+ // STATUS_PASSWORD_RESTRICTION for this.
+ //
+
+ Status = STATUS_PASSWORD_RESTRICTION;
+
+ //
+ // We never made it to the other machine, so we should continue
+ // trying to change the password.
+ //
+
+ *Authoritative = FALSE;
+ } else {
+ Status = (*NlpNetpApiStatusToNtStatus)( NetStatus );
+ }
+
+Cleanup:
+
+ //
+ // Free UserName if used.
+ //
+
+ if (UserName) {
+
+ RtlFreeHeap(MspHeap, 0, UserName);
+ }
+
+ //
+ // Free OldPassword if used. (Don't let password make it to page file)
+ //
+
+ if (OldPassword) {
+ RtlZeroMemory( OldPassword, wcslen(OldPassword)*sizeof(WCHAR) );
+ RtlFreeHeap(MspHeap, 0, OldPassword);
+ }
+
+ //
+ // Free NewPassword if used. (Don't let password make it to page file)
+ //
+
+ if (NewPassword) {
+ RtlZeroMemory( NewPassword, wcslen(NewPassword)*sizeof(WCHAR) );
+ RtlFreeHeap(MspHeap, 0, NewPassword);
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+MspChangePassword(
+ IN OUT PUNICODE_STRING ComputerName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING OldPassword,
+ IN PUNICODE_STRING NewPassword,
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN Impersonating,
+ OUT PDOMAIN_PASSWORD_INFORMATION *DomainPasswordInfo,
+ OUT PPOLICY_PRIMARY_DOMAIN_INFO *PrimaryDomainInfo OPTIONAL,
+ OUT PBOOLEAN Authoritative
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by MspLM20ChangePassword to change the password
+ on the specified server. The server may be either NT or Downlevel.
+
+Arguments:
+
+ ComputerName - Name of the target machine. This name may or may not
+ begin with two backslashes.
+
+ UserName - Name of the user to change password for.
+
+ OldPassword - Plaintext current password.
+
+ NewPassword - Plaintext replacement password.
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ DomainPasswordInfo - Password restriction information (returned only if
+ status is STATUS_PASSWORD_RESTRICTION).
+
+ PrimaryDomainInfo - DomainNameInformation (returned only if status is
+ STATUS_BACKUP_CONTROLLER).
+
+ Authoritative - Indicates that the error code is authoritative
+ and it indicates that password changing should stop. If false,
+ password changing should continue.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_PASSWORD_RESTRICTION - Password changing is restricted.
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING UncComputerName;
+
+ *Authoritative = TRUE;
+
+ //
+ // Ensure the server name is a UNC server name.
+ //
+
+ Status = MspAddBackslashesComputerName( ComputerName, &UncComputerName );
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("MspChangePassword: MspAddBackslashes..(%wZ) failed, status %x\n",
+ ComputerName, Status));
+ return(Status);
+ }
+
+
+ //
+ // Assume the Server is an NT server and try to change the password.
+ //
+
+ Status = MspChangePasswordSam(
+ &UncComputerName,
+ UserName,
+ OldPassword,
+ NewPassword,
+ ClientRequest,
+ Impersonating,
+ DomainPasswordInfo,
+ PrimaryDomainInfo,
+ Authoritative );
+
+ //
+ // If MspChangePasswordSam returns anything other than
+ // STATUS_CANT_ACCESS_DOMAIN_INFO, it was able to connect
+ // to the remote computer so we won't try downlevel.
+ //
+
+ if (Status == STATUS_CANT_ACCESS_DOMAIN_INFO) {
+
+ Status = MspChangePasswordDownlevel(
+ &UncComputerName,
+ UserName,
+ OldPassword,
+ NewPassword,
+ Authoritative );
+ }
+
+
+ //
+ // Free UncComputerName.Buffer if different from ComputerName.
+ //
+
+ if ( UncComputerName.Buffer != ComputerName->Buffer ) {
+ RtlFreeHeap(MspHeap, 0, UncComputerName.Buffer);
+ }
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+MspLm20ChangePassword (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of MsV1_0ChangePassword. This routine changes an
+ account's password by either calling SamXxxPassword (for NT domains) or
+ RxNetUserPasswordSet (for downlevel domains and standalone servers).
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_PASSWORD_RESTRICTION - The password change failed because the
+ - password doesn't meet one or more domain restrictions. The
+ - response buffer is allocated. If the PasswordInfoValid flag is
+ - set it contains valid information otherwise it contains no
+ - information because this was a down-level change.
+
+ STATUS_BACKUP_CONTROLLER - The named machine is a Backup Domain Controller.
+ Changing password is only allowed on the Primary Domain Controller.
+
+--*/
+
+{
+ PMSV1_0_CHANGEPASSWORD_REQUEST ChangePasswordRequest;
+ PMSV1_0_CHANGEPASSWORD_RESPONSE ChangePasswordResponse;
+ NTSTATUS Status;
+ NTSTATUS SavedStatus;
+ LPWSTR DomainName = NULL;
+ LPWSTR DCName = NULL;
+ UNICODE_STRING DCNameString;
+ NET_API_STATUS NetStatus;
+ PPOLICY_LSA_SERVER_ROLE_INFO PolicyLsaServerRoleInfo = NULL;
+ PDOMAIN_PASSWORD_INFORMATION DomainPasswordInfo = NULL;
+ PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo = NULL;
+ PWKSTA_INFO_100 WkstaInfo100 = NULL;
+ BOOLEAN PasswordBufferValidated = FALSE;
+ CLIENT_BUFFER_DESC ClientBufferDesc;
+ PSECURITY_SEED_AND_LENGTH SeedAndLength;
+ UCHAR Seed;
+ BOOLEAN Authoritative = FALSE;
+
+ RtlInitUnicodeString(
+ &DCNameString,
+ NULL
+ );
+ //
+ // Sanity checks.
+ //
+
+ NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
+
+ if ( SubmitBufferSize < sizeof(MSV1_0_CHANGEPASSWORD_REQUEST) ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ ChangePasswordRequest = (PMSV1_0_CHANGEPASSWORD_REQUEST) ProtocolSubmitBuffer;
+
+ ASSERT( ChangePasswordRequest->MessageType == MsV1_0ChangePassword ||
+ ChangePasswordRequest->MessageType == MsV1_0ChangeCachedPassword );
+
+ RELOCATE_ONE( &ChangePasswordRequest->DomainName );
+
+ RELOCATE_ONE( &ChangePasswordRequest->AccountName );
+
+ if ( ChangePasswordRequest->MessageType == MsV1_0ChangeCachedPassword ) {
+ NULL_RELOCATE_ONE( &ChangePasswordRequest->OldPassword );
+ } else {
+ RELOCATE_ONE_ENCODED( &ChangePasswordRequest->OldPassword );
+ }
+
+ RELOCATE_ONE_ENCODED( &ChangePasswordRequest->NewPassword );
+
+ SeedAndLength = (PSECURITY_SEED_AND_LENGTH) &ChangePasswordRequest->OldPassword.Length;
+ Seed = SeedAndLength->Seed;
+ SeedAndLength->Seed = 0;
+
+ if (Seed != 0) {
+
+ try {
+ RtlRunDecodeUnicodeString(
+ Seed,
+ &ChangePasswordRequest->OldPassword
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = STATUS_ILL_FORMED_PASSWORD;
+ goto Cleanup;
+ }
+ }
+
+ SeedAndLength = (PSECURITY_SEED_AND_LENGTH) &ChangePasswordRequest->NewPassword.Length;
+ Seed = SeedAndLength->Seed;
+ SeedAndLength->Seed = 0;
+
+ if (Seed != 0) {
+
+ try {
+ RtlRunDecodeUnicodeString(
+ Seed,
+ &ChangePasswordRequest->NewPassword
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = STATUS_ILL_FORMED_PASSWORD;
+ goto Cleanup;
+ }
+ }
+
+
+ *ReturnBufferSize = 0;
+ *ProtocolReturnBuffer = NULL;
+ *ProtocolStatus = STATUS_PENDING;
+ PasswordBufferValidated = TRUE;
+
+
+#ifdef COMPILED_BY_DEVELOPER
+
+ KdPrint(("MSV1_0:\n"
+ "\tDomain:\t%wZ\n"
+ "\tAccount:\t%wZ\n"
+ "\tOldPassword(%d)\n"
+ "\tNewPassword(%d)\n",
+ &ChangePasswordRequest->DomainName,
+ &ChangePasswordRequest->AccountName,
+ (int) ChangePasswordRequest->OldPassword.Length,
+ (int) ChangePasswordRequest->NewPassword.Length
+ ));
+
+#endif // COMPILED_BY_DEVELOPER
+
+ //
+ // If we're just changing the cached password,
+ // skip changing the password on the domain.
+ //
+
+ if ( ChangePasswordRequest->MessageType == MsV1_0ChangeCachedPassword ) {
+ Status = STATUS_SUCCESS;
+ goto PasswordChangeSuccessfull;
+ }
+
+
+ //
+ // This function uses Net apis, and so requires that Netapi.dll be loaded.
+ //
+
+ if (!NlpNetapiDllLoaded) {
+
+ NlpLoadNetapiDll();
+
+ if (!NlpNetapiDllLoaded) {
+ Status = STATUS_NO_SUCH_FILE;
+ goto Cleanup;
+ }
+ }
+
+
+
+ //
+ // Check to see if the name provided is a domain name. If it has no
+ // leading "\\" and does not match the name of the computer, it may be.
+ //
+
+
+
+
+ if ((( ChangePasswordRequest->DomainName.Length < 3 * sizeof(WCHAR)) ||
+ ( ChangePasswordRequest->DomainName.Buffer[0] != L'\\' &&
+ ChangePasswordRequest->DomainName.Buffer[1] != L'\\' ) ) &&
+ !RtlEqualDomainName(
+ &NlpComputerName,
+ &ChangePasswordRequest->DomainName )) {
+
+ //
+ // Check if we are PDC in this domain
+ //
+
+ if ( !NlpWorkstation &&
+ RtlEqualDomainName(
+ &NlpSamDomainName,
+ &ChangePasswordRequest->DomainName )) {
+
+ //
+ // We are a Domain Controller for the specified domain. If our
+ // role is PDC, we already have the PDC ComputerName. Otherwise,
+ // we need to call NlpNetGetDCName.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ NlpPolicyHandle,
+ PolicyLsaServerRoleInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyLsaServerRoleInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ if (PolicyLsaServerRoleInfo->LsaServerRole == PolicyServerRolePrimary) {
+
+ DCNameString = NlpComputerName;
+
+ }
+ }
+
+ if (DCNameString.Buffer == NULL) {
+
+ //
+ // Build a zero terminated domain name.
+ //
+
+ DomainName = RtlAllocateHeap(
+ MspHeap,
+ 0,
+ ChangePasswordRequest->DomainName.Length + sizeof(WCHAR));
+
+ if ( DomainName == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( DomainName,
+ ChangePasswordRequest->DomainName.Buffer,
+ ChangePasswordRequest->DomainName.Length );
+ DomainName[ChangePasswordRequest->DomainName.Length / sizeof(WCHAR)] = 0;
+
+ NetStatus = (*NlpNetGetDCName)(
+ NULL,
+ DomainName,
+ (LPBYTE *)&DCName );
+
+ if ( NetStatus != NERR_Success ) {
+ Status = STATUS_NO_SUCH_DOMAIN;
+ } else {
+ RtlInitUnicodeString( &DCNameString, DCName );
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = MspChangePassword(
+ &DCNameString,
+ &ChangePasswordRequest->AccountName,
+ &ChangePasswordRequest->OldPassword,
+ &ChangePasswordRequest->NewPassword,
+ ClientRequest,
+ ChangePasswordRequest->Impersonating,
+ &DomainPasswordInfo,
+ NULL,
+ &Authoritative );
+
+ if ( NT_SUCCESS(Status) || (Authoritative && (Status == STATUS_PASSWORD_RESTRICTION) ) ) {
+ goto PasswordChangeSuccessfull;
+ }
+ if ( !NT_SUCCESS(Status) && Authoritative) {
+ goto Cleanup;
+ }
+
+ }
+
+ }
+
+ //
+ // Change the password assuming the DomainName is really a server name
+ //
+ // The domain name is overloaded to be either a domain name or a server
+ // name. The server name is useful when changing the password on a LM2.x
+ // standalone server, which is a "member" of a domain but uses a private
+ // account database.
+ //
+
+ Status = MspChangePassword(
+ &ChangePasswordRequest->DomainName,
+ &ChangePasswordRequest->AccountName,
+ &ChangePasswordRequest->OldPassword,
+ &ChangePasswordRequest->NewPassword,
+ ClientRequest,
+ ChangePasswordRequest->Impersonating,
+ &DomainPasswordInfo,
+ &PrimaryDomainInfo,
+ &Authoritative );
+
+ //
+ // If we succeeded or we got back an authoritative password restriction
+ // then we are done trying.
+ //
+
+ if ( NT_SUCCESS(Status) || (Authoritative && (Status == STATUS_PASSWORD_RESTRICTION) ) ) {
+ goto PasswordChangeSuccessfull;
+ }
+
+ if ( !NT_SUCCESS(Status) && Authoritative) {
+ goto Cleanup;
+ }
+
+
+ //
+ // If the specified machine was a BDC in a domain,
+ // Pretend the caller passed us the domain name in the first place.
+ //
+
+ if ( Status == STATUS_BACKUP_CONTROLLER && PrimaryDomainInfo != NULL ) {
+
+ ChangePasswordRequest->DomainName = PrimaryDomainInfo->Name;
+ Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+
+ //
+ // If DomainName is actually a server name,
+ // just return the status to the caller.
+ //
+
+ if ( Authoritative &&
+ ( Status != STATUS_BAD_NETWORK_PATH ||
+ ( ChangePasswordRequest->DomainName.Length >= 3 * sizeof(WCHAR) &&
+ ChangePasswordRequest->DomainName.Buffer[0] == L'\\' &&
+ ChangePasswordRequest->DomainName.Buffer[1] == L'\\' ) ) ) {
+
+ //
+ // If \\xxx was specified, but xxx doesn't exist,
+ // return the status code that the DomainName field is bad.
+ //
+
+ if ( Status == STATUS_BAD_NETWORK_PATH ) {
+ Status = STATUS_NO_SUCH_DOMAIN;
+ }
+ goto Cleanup;
+
+ }
+
+
+ //
+ // Build a zero terminated domain name.
+ //
+
+ DomainName = RtlAllocateHeap(
+ MspHeap,
+ 0,
+ ChangePasswordRequest->DomainName.Length + sizeof(WCHAR));
+
+ if ( DomainName == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( DomainName,
+ ChangePasswordRequest->DomainName.Buffer,
+ ChangePasswordRequest->DomainName.Length );
+ DomainName[ChangePasswordRequest->DomainName.Length / sizeof(WCHAR)] = 0;
+
+
+ //
+ // If we are the PDC of the specified domain,
+ // don't bother calling NetGetDCName.
+ //
+
+ if ( !NlpWorkstation &&
+ RtlEqualDomainName(
+ &NlpSamDomainName,
+ &ChangePasswordRequest->DomainName )) {
+
+ //
+ // We are a Domain Controller for the specified domain. If our
+ // role is PDC, we already have the PDC ComputerName. Otherwise,
+ // we need to call NlpNetGetDCName.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ NlpPolicyHandle,
+ PolicyLsaServerRoleInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyLsaServerRoleInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ if (PolicyLsaServerRoleInfo->LsaServerRole == PolicyServerRolePrimary) {
+
+ //
+ // Just change the password locally.
+ //
+
+ Status = MspChangePassword(
+ &NlpComputerName,
+ &ChangePasswordRequest->AccountName,
+ &ChangePasswordRequest->OldPassword,
+ &ChangePasswordRequest->NewPassword,
+ ClientRequest,
+ ChangePasswordRequest->Impersonating,
+ &DomainPasswordInfo,
+ NULL,
+ &Authoritative );
+
+ if ( NT_SUCCESS(Status) || (Authoritative && (Status == STATUS_PASSWORD_RESTRICTION) ) ) {
+ goto PasswordChangeSuccessfull;
+ }
+
+ if ( !NT_SUCCESS(Status) && Authoritative) {
+ goto Cleanup;
+ }
+
+ }
+
+ }
+
+
+
+ //
+ // Determine the PDC of the named domain so we can change the password there.
+ //
+
+ NetStatus = (*NlpNetGetDCName)(
+ NULL,
+ DomainName,
+ (LPBYTE *)&DCName );
+
+ if ( NetStatus != NERR_Success ) {
+ Status = STATUS_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString( &DCNameString, DCName );
+
+ Status = MspChangePassword(
+ &DCNameString,
+ &ChangePasswordRequest->AccountName,
+ &ChangePasswordRequest->OldPassword,
+ &ChangePasswordRequest->NewPassword,
+ ClientRequest,
+ ChangePasswordRequest->Impersonating,
+ &DomainPasswordInfo,
+ NULL,
+ &Authoritative );
+
+ if ( NT_SUCCESS(Status) || (Authoritative && (Status == STATUS_PASSWORD_RESTRICTION) ) ) {
+ goto PasswordChangeSuccessfull;
+ }
+
+ if ( !NT_SUCCESS(Status) && Authoritative) {
+ goto Cleanup;
+ }
+
+ goto Cleanup;
+
+
+ //
+ // The password change was successfull, or there was a password restriction
+ //
+PasswordChangeSuccessfull:
+
+ //
+ // Allocate and initialize the response buffer.
+ //
+
+ SavedStatus = Status;
+
+ *ReturnBufferSize = sizeof(MSV1_0_CHANGEPASSWORD_RESPONSE);
+
+ Status = NlpAllocateClientBuffer( &ClientBufferDesc,
+ sizeof(MSV1_0_CHANGEPASSWORD_RESPONSE),
+ *ReturnBufferSize );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint(("MSV1_0: MspLm20ChangePassword: cannot alloc client buffer\n"));
+ *ReturnBufferSize = 0;
+ goto Cleanup;
+ }
+
+ ChangePasswordResponse = (PMSV1_0_CHANGEPASSWORD_RESPONSE) ClientBufferDesc.MsvBuffer;
+
+ ChangePasswordResponse->MessageType = MsV1_0ChangePassword;
+
+
+ //
+ // Copy the DomainPassword restrictions out to the caller depending on
+ // whether it was passed to us.
+ //
+ // Mark the buffer as valid or invalid to let the caller know.
+ //
+ // if STATUS_PASSWORD_RESTRICTION is returned. This status can be
+ // returned by either SAM or a down-level change. Only SAM will return
+ // valid data so we have a flag in the buffer that says whether the data
+ // is valid or not.
+ //
+
+ if ( DomainPasswordInfo == NULL ) {
+ ChangePasswordResponse->PasswordInfoValid = FALSE;
+ } else {
+ ChangePasswordResponse->DomainPasswordInfo = *DomainPasswordInfo;
+ ChangePasswordResponse->PasswordInfoValid = TRUE;
+ }
+
+
+ //
+ // Flush the buffer to the client's address space.
+ //
+
+ Status = NlpFlushClientBuffer( &ClientBufferDesc,
+ ProtocolReturnBuffer );
+
+
+
+ //
+ // Update cached credentials with the new password.
+ //
+ // This is done by calling NlpChangePassword,
+ // which takes encrypted passwords, so encrypt 'em.
+ //
+
+ if ( NT_SUCCESS(SavedStatus) ) {
+ ANSI_STRING LmPasswordString;
+ CHAR LmPassword[LM20_PWLEN+1];
+ LM_OWF_PASSWORD LmOwfPassword;
+ NT_OWF_PASSWORD NtOwfPassword;
+
+ LmPasswordString.Buffer = LmPassword;
+ LmPasswordString.Length = 0;
+ LmPasswordString.MaximumLength = sizeof( LmPassword );
+ Status = RtlUpcaseUnicodeStringToOemString( &LmPasswordString,
+ &ChangePasswordRequest->NewPassword,
+ FALSE
+ );
+ if ( NT_SUCCESS(Status) ) {
+ Status = RtlCalculateLmOwfPassword( LmPassword, &LmOwfPassword );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ Status = RtlCalculateNtOwfPassword( &ChangePasswordRequest->NewPassword,
+ &NtOwfPassword );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ //
+ // Failure of NlpChangePassword is OK, that means that the
+ // account we've been working with isn't the one we're
+ // caching credentials for.
+ //
+
+ (VOID) NlpChangePassword(
+ &ChangePasswordRequest->DomainName,
+ &ChangePasswordRequest->AccountName,
+ &LmOwfPassword,
+ &NtOwfPassword
+ );
+ }
+ }
+
+
+ Status = SavedStatus;
+
+
+Cleanup:
+
+
+ //
+ // Free Locally allocated resources
+ //
+
+ if (DomainName != NULL) {
+ RtlFreeHeap(MspHeap, 0, DomainName);
+ }
+
+ if ( DCName != NULL ) {
+ (*NlpNetApiBufferFree)(DCName);
+ }
+
+ if ( WkstaInfo100 != NULL ) {
+ (*NlpNetApiBufferFree)(WkstaInfo100);
+ }
+
+ if ( DomainPasswordInfo != NULL ) {
+ SamFreeMemory(DomainPasswordInfo);
+ }
+
+ if ( PrimaryDomainInfo != NULL ) {
+ (VOID) LsaFreeMemory( PrimaryDomainInfo );
+ }
+
+
+ //
+ // Free Policy Server Role Information if used.
+ //
+
+ if (PolicyLsaServerRoleInfo != NULL) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyLsaServerRoleInformation,
+ (PLSAPR_POLICY_INFORMATION) PolicyLsaServerRoleInfo
+ );
+ }
+
+ //
+ // Free the return buffer.
+ //
+
+ NlpFreeClientBuffer( &ClientBufferDesc );
+
+ //
+ // Don't let the password stay in the page file.
+ //
+
+ if ( PasswordBufferValidated ) {
+ RtlEraseUnicodeString( &ChangePasswordRequest->OldPassword );
+ RtlEraseUnicodeString( &ChangePasswordRequest->NewPassword );
+ }
+
+ //
+ // Return status to the caller.
+ //
+
+
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+}
diff --git a/private/lsa/msv1_0/msvsam.c b/private/lsa/msv1_0/msvsam.c
new file mode 100644
index 000000000..9974af875
--- /dev/null
+++ b/private/lsa/msv1_0/msvsam.c
@@ -0,0 +1,2091 @@
+/*++
+
+Copyright (c) 1987-1992 Microsoft Corporation
+
+Module Name:
+
+ msvsam.c
+
+Abstract:
+
+ Sam account validation interface.
+
+ These routines are shared by the MSV authentication package and
+ the Netlogon service.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 15-Jan-1992
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#include "msp.h"
+#include "nlp.h"
+#include <stddef.h> // offsetof()
+#include <rpc.h> // Needed by samrpc.h
+#include <samrpc.h> // Samr Routines
+#include <samisrv.h> // SamIFree routines
+#include <ssi.h> // SSI_ACCOUNT_POSTFIX_CHAR
+
+
+
+BOOLEAN
+MsvpPasswordValidate (
+ IN BOOLEAN UasCompatibilityRequired,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN PUSER_INTERNAL1_INFORMATION Passwords,
+ OUT PULONG UserFlags,
+ OUT PUSER_SESSION_KEY UserSessionKey,
+ OUT PLM_SESSION_KEY LmSessionKey
+)
+/*++
+
+Routine Description:
+
+ Process an interactive, network, or session logon. It calls
+ SamIUserValidation, validates the passed in credentials, updates the logon
+ statistics and packages the result for return to the caller.
+
+ This routine is called directly from the MSV Authentication package
+ on any system where LanMan is not installed. This routine is called
+ from the Netlogon Service otherwise.
+
+Arguments:
+
+ UasCompatibilityRequired -- True, if UAS compatibility is required.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on. The LogonDomainName field should be ignored.
+ The caller is responsible for validating this field.
+
+ Passwords -- Specifies the passwords for the user account.
+
+ UserFlags -- Returns flags identifying how the password was validated.
+ Returns LOGON_NOENCRYPTION if the password wasn't encrypted
+ Returns LOGON_USED_LM_PASSWORD if the LM password from SAM was used.
+
+ UserSessionKey -- Returns the NT User session key for this network logon
+ session.
+
+ LmSessionKey -- Returns the LM compatible session key for this network
+ logon session.
+
+Return Value:
+
+ TRUE -- Password validation is successful
+ FALSE -- Password validation failed
+
+--*/
+{
+ NTSTATUS Status;
+
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+ PNETLOGON_INTERACTIVE_INFO LogonInteractiveInfo;
+ PNETLOGON_NETWORK_INFO LogonNetworkInfo;
+ BOOLEAN AlreadyValidated = FALSE;
+
+ //
+ // Initialization.
+ //
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
+ *UserFlags = 0;
+ RtlZeroMemory( UserSessionKey, sizeof(*UserSessionKey) );
+ RtlZeroMemory( LmSessionKey, sizeof(*LmSessionKey) );
+
+
+ //
+ // Ensure the OWF password is always defined
+ //
+
+ if ( !Passwords->NtPasswordPresent ){
+ RtlCopyMemory( &Passwords->NtOwfPassword,
+ &NlpNullNtOwfPassword,
+ sizeof(Passwords->NtOwfPassword) );
+ }
+
+ if ( !Passwords->LmPasswordPresent ){
+ RtlCopyMemory( &Passwords->LmOwfPassword,
+ &NlpNullLmOwfPassword,
+ sizeof(Passwords->LmOwfPassword) );
+ }
+
+
+
+
+ //
+ // Handle interactive/service validation.
+ //
+ // Simply compare the OWF password passed in with the one from the
+ // SAM database.
+ //
+
+ switch ( LogonLevel ) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+
+ ASSERT( offsetof( NETLOGON_INTERACTIVE_INFO, LmOwfPassword)
+ == offsetof( NETLOGON_SERVICE_INFO, LmOwfPassword) );
+ ASSERT( offsetof( NETLOGON_INTERACTIVE_INFO, NtOwfPassword)
+ == offsetof( NETLOGON_SERVICE_INFO, NtOwfPassword) );
+
+ LogonInteractiveInfo =
+ (PNETLOGON_INTERACTIVE_INFO) LogonInformation;
+
+ //
+ // If we're in UasCompatibilityMode,
+ // and we don't have the NT password in SAM (but do have LM password),
+ // validate against the LM version of the password.
+ //
+
+ if ( UasCompatibilityRequired &&
+ !Passwords->NtPasswordPresent &&
+ Passwords->LmPasswordPresent ) {
+
+ if ( RtlCompareMemory( &Passwords->LmOwfPassword,
+ &LogonInteractiveInfo->LmOwfPassword,
+ LM_OWF_PASSWORD_LENGTH ) !=
+ LM_OWF_PASSWORD_LENGTH ) {
+
+ return FALSE;
+ }
+ *UserFlags |= LOGON_USED_LM_PASSWORD;
+
+ //
+ // In all other circumstances, use the NT version of the password.
+ // This enforces case sensitivity.
+ //
+
+ } else {
+
+ if ( RtlCompareMemory( &Passwords->NtOwfPassword,
+ &LogonInteractiveInfo->NtOwfPassword,
+ NT_OWF_PASSWORD_LENGTH ) !=
+ NT_OWF_PASSWORD_LENGTH ) {
+
+ return FALSE;
+ }
+ }
+
+ break;
+
+
+ //
+ // Handle network logon validation.
+ //
+
+ case NetlogonNetworkInformation:
+
+ //
+ // First, assume the passed password information is a challenge
+ // response.
+ //
+
+
+ LogonNetworkInfo =
+ (PNETLOGON_NETWORK_INFO) LogonInformation;
+
+
+ //
+ // If we're in UasCompatibilityMode,
+ // and we don't have the NT password in SAM (but do have LM password)or
+ // we don't have the NT password supplied by the caller.
+ // validate against the LM version of the password.
+ //
+
+ if ( UasCompatibilityRequired &&
+ ((!Passwords->NtPasswordPresent && Passwords->LmPasswordPresent) ||
+ LogonNetworkInfo->NtChallengeResponse.Length !=
+ NT_RESPONSE_LENGTH) ) {
+
+ LM_RESPONSE LmResponse;
+
+
+ //
+ // First check the length of the password information is
+ // the length of a challenge response.
+ //
+ if ( LogonNetworkInfo->LmChallengeResponse.Length ==
+ LM_RESPONSE_LENGTH ) {
+
+ //
+ // Compute what the response should be.
+ //
+
+ Status = RtlCalculateLmResponse(
+ &LogonNetworkInfo->LmChallenge,
+ &Passwords->LmOwfPassword,
+ &LmResponse );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // If the responses match, the passwords are valid.
+ //
+
+ if ( RtlCompareMemory(
+ LogonNetworkInfo->
+ LmChallengeResponse.Buffer,
+ &LmResponse,
+ LM_RESPONSE_LENGTH ) ==
+ LM_RESPONSE_LENGTH ) {
+
+ AlreadyValidated = TRUE;
+ *UserFlags |= LOGON_USED_LM_PASSWORD;
+ }
+ }
+ }
+
+
+ //
+ // In all other circumstances, use the NT version of the password.
+ // This enforces case sensitivity.
+ //
+
+ } else {
+
+ NT_RESPONSE NtResponse;
+
+
+ //
+ // First check the length of the password information is
+ // the length of a challenge response.
+ //
+ if ( LogonNetworkInfo->NtChallengeResponse.Length ==
+ NT_RESPONSE_LENGTH ) {
+
+ //
+ // Compute what the response should be.
+ //
+
+ Status = RtlCalculateNtResponse(
+ &LogonNetworkInfo->LmChallenge,
+ &Passwords->NtOwfPassword,
+ &NtResponse );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // If the responses match, the passwords are valid.
+ //
+
+ if ( RtlCompareMemory(
+ LogonNetworkInfo->
+ NtChallengeResponse.Buffer,
+ &NtResponse,
+ NT_RESPONSE_LENGTH ) ==
+ NT_RESPONSE_LENGTH ) {
+
+ AlreadyValidated = TRUE;
+ }
+ }
+ }
+ }
+
+
+ //
+ // If we haven't already validated this user,
+ // Validate a Cleartext password on a Network logon request.
+ //
+
+ if ( !AlreadyValidated ) {
+
+ // If Cleartext passwords are not allowed,
+ // indicate the password doesn't match.
+ //
+
+ if((LogonInfo->ParameterControl & CLEARTEXT_PASSWORD_ALLOWED) == 0){
+ return FALSE;
+ }
+
+
+ //
+ // Compute the OWF password for the specified Cleartext password and
+ // compare that to the OWF password retrieved from SAM.
+ //
+
+ //
+ // If we're in UasCompatibilityMode,
+ // and we don't have the NT password in SAM or
+ // we don't have the NT password supplied by the caller.
+ // validate against the LM version of the password.
+ //
+
+ if ( UasCompatibilityRequired &&
+ (!Passwords->NtPasswordPresent ||
+ LogonNetworkInfo->NtChallengeResponse.Length == 0 ) ) {
+
+ LM_OWF_PASSWORD LmOwfPassword;
+ CHAR LmPassword[LM20_PWLEN+1];
+ USHORT i;
+
+
+ //
+ // Compute the LmOwfPassword for the cleartext password passed in.
+ // (Enforce length restrictions on LanMan compatible passwords.)
+ //
+
+ if ( LogonNetworkInfo->LmChallengeResponse.Length >
+ sizeof(LmPassword) ) {
+ return FALSE;
+ }
+
+ RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
+
+ for (i = 0; i < LogonNetworkInfo->LmChallengeResponse.Length; i++) {
+ LmPassword[i] =
+ RtlUpperChar(LogonNetworkInfo->LmChallengeResponse.Buffer[i]);
+ }
+
+ (VOID) RtlCalculateLmOwfPassword( LmPassword, &LmOwfPassword );
+
+ if ( RtlCompareMemory( &Passwords->LmOwfPassword,
+ &LmOwfPassword,
+ LM_OWF_PASSWORD_LENGTH ) !=
+ LM_OWF_PASSWORD_LENGTH ) {
+
+ //
+ // Try the case preserved clear text password, too.
+ // (I know of no client that does this,
+ // but it is compatible with the LM 2.x server.)
+ //
+
+ RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
+ RtlCopyMemory(
+ &LmPassword,
+ LogonNetworkInfo->LmChallengeResponse.Buffer,
+ LogonNetworkInfo->LmChallengeResponse.Length);
+
+ (VOID) RtlCalculateLmOwfPassword( LmPassword,
+ &LmOwfPassword );
+
+ if ( RtlCompareMemory( &Passwords->LmOwfPassword,
+ &LmOwfPassword,
+ LM_OWF_PASSWORD_LENGTH ) !=
+ LM_OWF_PASSWORD_LENGTH ) {
+
+ return FALSE;
+ }
+
+ }
+
+ *UserFlags |= LOGON_USED_LM_PASSWORD;
+
+
+ //
+ // In all other circumstances, use the NT version of the password.
+ // This enforces case sensitivity.
+ //
+
+ } else {
+ NT_OWF_PASSWORD NtOwfPassword;
+
+
+ //
+ // Compute the NtOwfPassword for the cleartext password passed in.
+ //
+
+ Status = RtlCalculateNtOwfPassword(
+ (PUNICODE_STRING)
+ &LogonNetworkInfo->NtChallengeResponse,
+ &NtOwfPassword );
+
+ if ( RtlCompareMemory( &Passwords->NtOwfPassword,
+ &NtOwfPassword,
+ NT_OWF_PASSWORD_LENGTH ) !=
+ NT_OWF_PASSWORD_LENGTH ) {
+
+ return FALSE;
+ }
+ }
+
+ *UserFlags |= LOGON_NOENCRYPTION;
+ }
+
+ //
+ // ASSERT: the network logon has been authenticated
+ //
+ // Compute the session keys.
+
+ //
+ // If the client negotiated a non-NT protocol,
+ // use the lanman session key as the UserSessionKey.
+ //
+
+ if ( LogonNetworkInfo->NtChallengeResponse.Length == 0 ) {
+
+ ASSERT( sizeof(*UserSessionKey) >= sizeof(*LmSessionKey) );
+
+ RtlCopyMemory( UserSessionKey,
+ &Passwords->LmOwfPassword,
+ sizeof(*LmSessionKey) );
+
+ } else {
+
+ //
+ // Return the NT UserSessionKey unless this is an account
+ // that doesn't have the NT version of the password.
+ // (A null password counts as a password).
+ //
+
+ if ( Passwords->NtPasswordPresent || !Passwords->LmPasswordPresent){
+
+ Status = RtlCalculateUserSessionKeyNt(
+ (PNT_RESPONSE) NULL, // Argument not used
+ &Passwords->NtOwfPassword,
+ UserSessionKey );
+
+ ASSERT( NT_SUCCESS(Status) );
+ }
+ }
+
+ //
+ // Return the LM SessionKey unless this is an account
+ // that doesn't have the LM version of the password.
+ // (A null password counts as a password).
+ //
+
+ if ( Passwords->LmPasswordPresent || !Passwords->NtPasswordPresent ) {
+ RtlCopyMemory( LmSessionKey,
+ &Passwords->LmOwfPassword,
+ sizeof(*LmSessionKey) );
+ }
+
+ break;
+
+ //
+ // Any other LogonLevel is an internal error.
+ //
+ default:
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+
+
+NTSTATUS
+MsvpSamValidate (
+ IN SAMPR_HANDLE DomainHandle,
+ IN BOOLEAN UasCompatibilityRequired,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
+ IN PUNICODE_STRING LogonServer,
+ IN PUNICODE_STRING LogonDomainName,
+ IN PSID LogonDomainId,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN ULONG GuestRelativeId,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ OUT PVOID * ValidationInformation,
+ OUT PBOOLEAN Authoritative,
+ OUT PBOOLEAN BadPasswordCountZeroed
+)
+/*++
+
+Routine Description:
+
+ Process an interactive, network, or session logon. It calls
+ SamIUserValidation, validates the passed in credentials, updates the logon
+ statistics and packages the result for return to the caller.
+
+ This routine is called by MsvSamValidate.
+
+Arguments:
+
+ DomainHandle -- Specifies a handle to the SamDomain to use to
+ validate the request.
+
+ UasCompatibilityRequired -- TRUE iff UasCompatibilityMode is on.
+
+ SecureChannelType -- The secure channel type this request was made on.
+
+ LogonServer -- Specifies the server name of the caller.
+
+ LogonDomainName -- Specifies the domain of the caller.
+
+ LogonDomainId -- Specifies the DomainId of the domain of the caller.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on. The LogonDomainName field should be ignored.
+ The caller is responsible for validating this field.
+
+ GuestRelativeId - If non-zero, specifies the relative ID of the account
+ to validate against.
+
+ ValidationLevel -- Specifies the level of information returned in
+ ValidationInformation. Must be NetlogonValidationSamInfo or
+ NetlogonValidationSamInfo2.
+
+ ValidationInformation -- Returns the requested validation
+ information. This buffer must be freed user MIDL_user_free.
+ This information is only return on STATUS_SUCCESS.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+ BadPasswordCountZeroed - Returns TRUE iff we zeroed the BadPasswordCount
+ field of this user.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+ STATUS_INVALID_INFO_CLASS: LogonLevel or ValidationLevel are invalid.
+ STATUS_NO_SUCH_USER: The specified user has no account.
+ STATUS_WRONG_PASSWORD: The password was invalid.
+
+ Other return codes from SamIUserValidation
+
+--*/
+{
+ NTSTATUS Status;
+
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+
+ SAMPR_HANDLE UserHandle = NULL;
+ ULONG RelativeId = GuestRelativeId;
+
+ PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL;
+ PSAMPR_USER_ALL_INFORMATION UserAll;
+ PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL;
+ ULONG UserFlags = 0;
+ USER_SESSION_KEY UserSessionKey;
+ LM_SESSION_KEY LmSessionKey;
+ ULONG WhichFields = 0;
+ ULONG LocalUserFlags = 0;
+ ULONG Flags = 0;
+ ULONG DllNumber;
+
+ UNICODE_STRING LocalWorkstation;
+ ULONG UserAccountControl;
+ LARGE_INTEGER LogonTime;
+ LARGE_INTEGER LogoffTime;
+ LARGE_INTEGER KickoffTime;
+
+ LARGE_INTEGER AccountExpires;
+ LARGE_INTEGER PasswordMustChange;
+ LARGE_INTEGER PasswordLastSet;
+
+
+ PNETLOGON_VALIDATION_SAM_INFO2 ValidationSam = NULL;
+ ULONG ValidationSamSize;
+ PUCHAR Where;
+
+ SAMPR_ULONG_ARRAY RelativeIdArray;
+ SAMPR_ULONG_ARRAY UseArray;
+
+ //
+ // Initialization.
+ //
+
+ RelativeIdArray.Count = 1;
+ RelativeIdArray.Element = NULL;
+ UseArray.Count = 1;
+ UseArray.Element = NULL;
+ *BadPasswordCountZeroed = FALSE;
+
+ (VOID) NtQuerySystemTime( &LogonTime );
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
+
+
+ //
+ // Determine what account types are valid.
+ //
+ // Normal user accounts are always allowed.
+ //
+
+ UserAccountControl = USER_NORMAL_ACCOUNT;
+
+ *Authoritative = TRUE;
+
+ switch ( LogonLevel ) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+
+ break;
+
+ case NetlogonNetworkInformation:
+ //
+ // Local user (Temp Duplicate) accounts are only used on the machine
+ // being directly logged onto.
+ // (Nor are interactive or service logons allowed to them.)
+ //
+
+ if ( SecureChannelType == MsvApSecureChannel ) {
+ UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
+ }
+
+ //
+ // Machine accounts can be accessed on network connections.
+ //
+
+ UserAccountControl |= USER_INTERDOMAIN_TRUST_ACCOUNT |
+ USER_WORKSTATION_TRUST_ACCOUNT |
+ USER_SERVER_TRUST_ACCOUNT;
+
+ break;
+
+ default:
+ *Authoritative = TRUE;
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+
+ //
+ // Check the ValidationLevel
+ //
+
+ switch (ValidationLevel) {
+ case NetlogonValidationSamInfo:
+ case NetlogonValidationSamInfo2:
+ break;
+
+ default:
+
+ *Authoritative = TRUE;
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+
+
+
+ //
+ // Convert the user name to a RelativeId.
+ //
+
+ if ( RelativeId == 0 ) {
+ Status = SamrLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ (PRPC_UNICODE_STRING)&LogonInfo->UserName,
+ &RelativeIdArray,
+ &UseArray );
+
+ if ( !NT_SUCCESS(Status) ) {
+ RelativeIdArray.Element = NULL;
+ UseArray.Element = NULL;
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+ if ( UseArray.Element[0] != SidTypeUser ) {
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+ RelativeId = RelativeIdArray.Element[0];
+ }
+
+
+
+ //
+ // Open the user account.
+ //
+
+ Status = SamrOpenUser( DomainHandle,
+ USER_READ_GENERAL | USER_READ_PREFERENCES |
+ USER_READ_LOGON,
+ RelativeId,
+ &UserHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ UserHandle = NULL;
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+
+ //
+ // Get logon info from SAM.
+ //
+ // Status codes include STATUS_INTERNAL_DB_CORRUPTION,
+ // STATUS_INTERNAL_ERROR, or other internal errors where SAM
+ // isn't making an authoritative statement.
+ //
+
+ Status = SamrQueryInformationUser(
+ UserHandle,
+ UserAllInformation,
+ &UserAllInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ UserAllInfo = NULL;
+ *Authoritative = FALSE;
+ goto Cleanup;
+ }
+
+ UserAll = &UserAllInfo->All;
+
+
+
+ //
+ // If the account type isn't allowed,
+ // Treat this as though the User Account doesn't exist.
+ //
+ // SubAuthentication packages can be more specific than this test but
+ // they can't become less restrictive.
+ //
+
+ if ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) {
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+ //
+ // Ensure the account isn't locked out.
+ //
+
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED ) {
+
+ if (RelativeId != DOMAIN_USER_RID_ADMIN) {
+
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ *Authoritative = FALSE;
+ } else {
+ *Authoritative = TRUE;
+ }
+ Status = STATUS_ACCOUNT_LOCKED_OUT;
+ goto Cleanup;
+ } else {
+ PSAMPR_DOMAIN_INFO_BUFFER DomainInfo = NULL;
+ PDOMAIN_PASSWORD_INFORMATION DomainPassword;
+
+ //
+ // For administrators we need to check domain password
+ // policy to see if they can be locked out. If they can
+ // be locked out then they can only log on from a domain
+ // controller.
+ //
+
+ Status = SamrQueryInformationDomain(
+ DomainHandle,
+ DomainPasswordInformation,
+ &DomainInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ *Authoritative = FALSE;
+ goto Cleanup;
+ }
+
+ DomainPassword = &DomainInfo->Password;
+
+ if ((DomainPassword->PasswordProperties & DOMAIN_LOCKOUT_ADMINS) != 0) {
+ //
+ // Administrators are supposed to be locked out if they are
+ // not logging on locally
+ //
+
+ if (!RtlEqualUnicodeString(
+ &NlpComputerName,
+ &LogonInfo->Workstation,
+ FALSE) ||
+ (LogonLevel != NetlogonInteractiveInformation)) {
+
+ //
+ // Admin accounts can't be disabled, so this is always
+ // authoritative.
+ //
+
+ *Authoritative = TRUE;
+
+ Status = STATUS_ACCOUNT_LOCKED_OUT;
+ }
+ }
+
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainInfo, DomainPasswordInformation );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ }
+ }
+
+
+
+
+ //
+ // If there is a SubAuthentication DLL,
+ // call it to do all the authentication work.
+ //
+
+ if ( LogonInfo->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL ) {
+
+ if ( SecureChannelType != MsvApSecureChannel ) {
+ Flags |= MSV1_0_PASSTHRU;
+ }
+ if ( GuestRelativeId != 0 ) {
+ Flags |= MSV1_0_GUEST_LOGON;
+ }
+
+ Status = Msv1_0SubAuthenticationRoutine(
+ LogonLevel,
+ LogonInformation,
+ Flags,
+ (PUSER_ALL_INFORMATION) UserAll,
+ &WhichFields,
+ &LocalUserFlags,
+ Authoritative,
+ &LogoffTime,
+ &KickoffTime );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Sanity check what the SubAuthentication package returned
+ //
+ if ( (WhichFields & ~USER_ALL_PARAMETERS) != 0 ) {
+ Status = STATUS_INTERNAL_ERROR;
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+
+ UserFlags |= LocalUserFlags;
+
+
+ //
+ // If there isn't a SubAuthenication DLL,
+ // do everything in-line.
+ //
+
+ } else {
+
+ //
+ // Check the password.
+ //
+
+ if ( SecureChannelType != NullSecureChannel ) {
+ USER_INTERNAL1_INFORMATION Passwords;
+
+ //
+ // Copy the password info to the right structure.
+ //
+
+ Passwords.NtPasswordPresent = UserAll->NtPasswordPresent;
+ if ( UserAll->NtPasswordPresent ) {
+ Passwords.NtOwfPassword =
+ *((PNT_OWF_PASSWORD)(UserAll->NtOwfPassword.Buffer));
+ }
+
+ Passwords.LmPasswordPresent = UserAll->LmPasswordPresent;
+ if ( UserAll->LmPasswordPresent ) {
+ Passwords.LmOwfPassword =
+ *((PLM_OWF_PASSWORD)(UserAll->LmOwfPassword.Buffer));
+ }
+
+
+ //
+ // If the password specified doesn't match the SAM password,
+ // then we've got a password mismatch.
+ //
+
+ if ( ! MsvpPasswordValidate (
+ UasCompatibilityRequired,
+ LogonLevel,
+ LogonInformation,
+ &Passwords,
+ &UserFlags,
+ &UserSessionKey,
+ &LmSessionKey ) ) {
+
+ //
+ // If this is a guest logon and the guest account has no password,
+ // let the user log on.
+ //
+ // This special case check is after the MsvpPasswordValidate to
+ // give MsvpPasswordValidate every opportunity to compute the
+ // correct values for UserSessionKey and LmSessionKey.
+ //
+
+ if ( GuestRelativeId != 0 &&
+ !UserAll->NtPasswordPresent &&
+ !UserAll->LmPasswordPresent ) {
+
+ RtlZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
+ RtlZeroMemory( &LmSessionKey, sizeof(LmSessionKey) );
+
+
+ //
+ // The password mismatched. We treat STATUS_WRONG_PASSWORD as
+ // an authoritative response. Our caller may choose to do otherwise.
+ //
+
+ } else {
+
+ Status = STATUS_WRONG_PASSWORD;
+
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ *Authoritative = FALSE;
+ } else {
+ *Authoritative = TRUE;
+ }
+
+ goto Cleanup;
+ }
+ }
+
+ //
+ // If the account is a machine account,
+ // let the caller know he got the password right.
+ // (But don't let him actually log on).
+ //
+
+ if ( (UserAll->UserAccountControl & USER_MACHINE_ACCOUNT_MASK) != 0 ) {
+ if (UserAll->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) {
+ Status = STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+ } else if (UserAll->UserAccountControl &
+ USER_WORKSTATION_TRUST_ACCOUNT) {
+ Status = STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+ } else if (UserAll->UserAccountControl & USER_SERVER_TRUST_ACCOUNT){
+ //
+ // If we are asked to allow logging on to a server trust account
+ // and the account is a server trust account, make sure that the
+ // user name equals the workstation name and the password on the
+ // account is the same as the workstation name.
+ //
+
+ if ((LogonInfo->ParameterControl & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT) != 0) {
+
+ NT_OWF_PASSWORD WorkstationPassword;
+ UNICODE_STRING TempUserName;
+ UNICODE_STRING TempMachineName;
+ WCHAR UserNameBuffer[CNLEN+1];
+
+ //
+ // If someone passed in this bit we need to make
+ // sure that the user name they passed was the same
+ // as the workstation name.
+ //
+
+ TempUserName = LogonInfo->UserName;
+ TempMachineName = LogonInfo->Workstation;
+
+ //
+ // Skip UNC marks on the machine name
+ //
+
+ if (TempMachineName.Length >= 2*sizeof(WCHAR) &&
+ (TempMachineName.Buffer[0] == L'\\') &&
+ (TempMachineName.Buffer[1] == L'\\')) {
+ TempMachineName.Buffer += 2;
+ TempMachineName.Length -= 2 * sizeof(WCHAR);
+ }
+
+ if ((TempUserName.Length > sizeof(WCHAR)) &&
+ (TempUserName.Buffer[TempUserName.Length/sizeof(WCHAR) - 1] ==
+ SSI_ACCOUNT_NAME_POSTFIX_CHAR)) {
+ TempUserName.Length -= 2;
+
+ if (RtlEqualUnicodeString(
+ &TempUserName,
+ &LogonInfo->Workstation,
+ TRUE // case insensitive
+ )) {
+
+ //
+ // Let the client know that this was
+ // a server trust account
+ //
+
+
+ UserFlags |= LOGON_SERVER_TRUST_ACCOUNT;
+
+ //
+ // Calculate the default password for a trust account,
+ // which is just the lowercase computer name
+ //
+
+ RtlCopyMemory(
+ UserNameBuffer,
+ TempUserName.Buffer,
+ TempUserName.Length
+ );
+
+ UserNameBuffer[TempUserName.Length/sizeof(WCHAR)] = L'\0';
+ TempUserName.Buffer = UserNameBuffer;
+
+ Status = RtlDowncaseUnicodeString(
+ &TempUserName,
+ &TempUserName,
+ FALSE
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlCalculateNtOwfPassword(
+ &TempUserName,
+ &WorkstationPassword
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // If the password does equal the server name, fail
+ // here.
+ //
+
+ if (RtlEqualNtOwfPassword(
+ &WorkstationPassword,
+ (PNT_OWF_PASSWORD) UserAll->NtOwfPassword.Buffer
+ )) {
+ Status = STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+
+ } else {
+ Status = STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+ }
+ } else {
+ Status = STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+ }
+ if (!NT_SUCCESS(Status)) {
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+ }
+ }
+
+ //
+ // Prevent some things from effecting the Administrator user
+ //
+
+ if (RelativeId != DOMAIN_USER_RID_ADMIN) {
+
+ //
+ // Check if the account is disabled.
+ //
+
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+ *Authoritative = FALSE;
+ Status = STATUS_ACCOUNT_DISABLED;
+ goto Cleanup;
+ }
+
+ //
+ // Check if the account has expired.
+ //
+
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->AccountExpires, AccountExpires );
+
+ if ( AccountExpires.QuadPart != 0 &&
+ LogonTime.QuadPart >= AccountExpires.QuadPart ) {
+ *Authoritative = TRUE;
+ Status = STATUS_ACCOUNT_EXPIRED;
+ goto Cleanup;
+ }
+
+
+ //
+ // The password is valid, check to see if the password is expired.
+ // (SAM will have appropriately set PasswordMustChange to reflect
+ // USER_DONT_EXPIRE_PASSWORD)
+ //
+ // Don't check if the password is expired is we didn't check the password.
+ //
+
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->PasswordMustChange, PasswordMustChange );
+ OLD_TO_NEW_LARGE_INTEGER( UserAll->PasswordLastSet, PasswordLastSet );
+
+ if ( SecureChannelType != NullSecureChannel ) {
+ if ( LogonTime.QuadPart >= PasswordMustChange.QuadPart ) {
+
+ if ( PasswordLastSet.QuadPart == 0 ) {
+ Status = STATUS_PASSWORD_MUST_CHANGE;
+ } else {
+ Status = STATUS_PASSWORD_EXPIRED;
+ }
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+ }
+ }
+
+ //
+ // Validate the workstation the user logged on from.
+ //
+ // Ditch leading \\ on workstation name before passing it to SAM.
+ //
+
+ LocalWorkstation = LogonInfo->Workstation;
+ if ( LocalWorkstation.Length > 0 &&
+ LocalWorkstation.Buffer[0] == L'\\' &&
+ LocalWorkstation.Buffer[1] == L'\\' ) {
+ LocalWorkstation.Buffer += 2;
+ LocalWorkstation.Length -= 2*sizeof(WCHAR);
+ LocalWorkstation.MaximumLength -= 2*sizeof(WCHAR);
+ }
+
+
+ //
+ // Check if SAM found some more specific reason to not allow logon.
+ //
+
+ Status = SamIAccountRestrictions(
+ UserHandle,
+ &LocalWorkstation,
+ (PUNICODE_STRING) &UserAll->WorkStations,
+ (PLOGON_HOURS) &UserAll->LogonHours,
+ &LogoffTime,
+ &KickoffTime );
+
+ if ( !NT_SUCCESS(Status) ) {
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+
+
+ //
+ // If there is a SubAuthentication package zero, call it
+ //
+
+ if (NlpSubAuthZeroExists) {
+ if ( SecureChannelType != MsvApSecureChannel ) {
+ Flags |= MSV1_0_PASSTHRU;
+ }
+ if ( GuestRelativeId != 0 ) {
+ Flags |= MSV1_0_GUEST_LOGON;
+ }
+
+
+ Status = Msv1_0SubAuthenticationRoutine(
+ LogonLevel,
+ LogonInformation,
+ Flags,
+ (PUSER_ALL_INFORMATION) UserAll,
+ &WhichFields,
+ &LocalUserFlags,
+ Authoritative,
+ &LogoffTime,
+ &KickoffTime );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Sanity check what the SubAuthentication package returned
+ //
+
+ if ( (WhichFields & ~USER_ALL_PARAMETERS) != 0 ) {
+ Status = STATUS_INTERNAL_ERROR;
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+
+ UserFlags |= LocalUserFlags;
+
+ }
+
+
+ }
+
+
+ //
+ // Get the group membership from SAM.
+ //
+
+ Status = SamrGetGroupsForUser(
+ UserHandle,
+ &GroupsBuffer );
+
+ if ( !NT_SUCCESS(Status) ) {
+ GroupsBuffer = NULL;
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+
+ //
+ // Allocate a return buffer for validation information.
+ // (Return less information for a network logon)
+ // (Return UserParameters for a MNS logon)
+ //
+
+ ValidationSamSize = sizeof( NETLOGON_VALIDATION_SAM_INFO2 ) +
+ GroupsBuffer->MembershipCount * sizeof(GROUP_MEMBERSHIP) +
+ LogonDomainName->Length + sizeof(WCHAR) +
+ LogonServer->Length + sizeof(WCHAR) +
+ RtlLengthSid( LogonDomainId );
+
+ if ( LogonLevel != NetlogonNetworkInformation ) {
+ ValidationSamSize +=
+ UserAll->UserName.Length + sizeof(WCHAR) +
+ UserAll->FullName.Length + sizeof(WCHAR) +
+ UserAll->ScriptPath.Length + sizeof(WCHAR)+
+ UserAll->ProfilePath.Length + sizeof(WCHAR) +
+ UserAll->HomeDirectory.Length + sizeof(WCHAR) +
+ UserAll->HomeDirectoryDrive.Length + sizeof(WCHAR);
+ }
+
+ if ( LogonInfo->ParameterControl & MSV1_0_RETURN_USER_PARAMETERS ) {
+ ValidationSamSize +=
+ UserAll->Parameters.Length + sizeof(WCHAR);
+ }
+
+ ValidationSamSize = ROUND_UP_COUNT( ValidationSamSize, sizeof(WCHAR) );
+
+ ValidationSam = MIDL_user_allocate( ValidationSamSize );
+
+ if ( ValidationSam == NULL ) {
+ *Authoritative = FALSE;
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Default unused fields (and ExpansionRoom) to zero.
+ //
+
+ RtlZeroMemory( ValidationSam, ValidationSamSize );
+
+ //
+ // Copy the scalars to the validation buffer.
+ //
+
+ Where = (PUCHAR) (ValidationSam + 1);
+
+ ValidationSam->LogonTime = UserAll->LastLogon;
+
+ NEW_TO_OLD_LARGE_INTEGER( LogoffTime, ValidationSam->LogoffTime );
+ NEW_TO_OLD_LARGE_INTEGER( KickoffTime, ValidationSam->KickOffTime );
+
+ ValidationSam->PasswordLastSet = UserAll->PasswordLastSet;
+ ValidationSam->PasswordCanChange = UserAll->PasswordCanChange;
+ ValidationSam->PasswordMustChange = UserAll->PasswordMustChange;
+
+ ValidationSam->LogonCount = UserAll->LogonCount;
+ ValidationSam->BadPasswordCount = UserAll->BadPasswordCount;
+ ValidationSam->UserId = UserAll->UserId;
+ ValidationSam->PrimaryGroupId = UserAll->PrimaryGroupId;
+ ValidationSam->GroupCount = GroupsBuffer->MembershipCount;
+ ValidationSam->UserFlags = UserFlags;
+ ValidationSam->UserSessionKey = UserSessionKey;
+ ASSERT( SAMINFO_LM_SESSION_KEY_SIZE == sizeof(LmSessionKey) );
+ RtlCopyMemory( &ValidationSam->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
+ &LmSessionKey,
+ SAMINFO_LM_SESSION_KEY_SIZE );
+
+ //
+ // If the client asked for extra information, return that
+ // we support it
+ //
+
+ if (ValidationLevel == NetlogonValidationSamInfo2) {
+ ValidationSam->UserFlags |= LOGON_EXTRA_SIDS;
+ }
+
+ //
+ // Copy ULONG aligned data to the validation buffer.
+ //
+
+ RtlCopyMemory(
+ Where,
+ GroupsBuffer->Groups,
+ GroupsBuffer->MembershipCount * sizeof(GROUP_MEMBERSHIP) );
+
+ ValidationSam->GroupIds = (PGROUP_MEMBERSHIP) Where;
+ Where += GroupsBuffer->MembershipCount * sizeof(GROUP_MEMBERSHIP);
+
+
+ RtlCopyMemory(
+ Where,
+ LogonDomainId,
+ RtlLengthSid( LogonDomainId ) );
+
+ ValidationSam->LogonDomainId = (PSID) Where;
+ Where += RtlLengthSid( LogonDomainId );
+
+
+ //
+ // Copy WCHAR aligned data to the validation buffer.
+ // (Return less information for a network logon)
+ //
+
+ Where = ROUND_UP_POINTER( Where, sizeof(WCHAR) );
+
+ if ( LogonLevel != NetlogonNetworkInformation ) {
+
+ NlpPutString( &ValidationSam->EffectiveName,
+ (PUNICODE_STRING)&UserAll->UserName,
+ &Where );
+
+ NlpPutString( &ValidationSam->FullName,
+ (PUNICODE_STRING)&UserAll->FullName,
+ &Where );
+
+ NlpPutString( &ValidationSam->LogonScript,
+ (PUNICODE_STRING)&UserAll->ScriptPath,
+ &Where );
+
+ NlpPutString( &ValidationSam->ProfilePath,
+ (PUNICODE_STRING)&UserAll->ProfilePath,
+ &Where );
+
+ NlpPutString( &ValidationSam->HomeDirectory,
+ (PUNICODE_STRING)&UserAll->HomeDirectory,
+ &Where );
+
+ NlpPutString( &ValidationSam->HomeDirectoryDrive,
+ (PUNICODE_STRING)&UserAll->HomeDirectoryDrive,
+ &Where );
+
+ }
+
+ NlpPutString( &ValidationSam->LogonServer,
+ LogonServer,
+ &Where );
+
+ NlpPutString( &ValidationSam->LogonDomainName,
+ LogonDomainName,
+ &Where );
+
+ //
+ // Kludge: Pass back UserParameters in HomeDirectoryDrive since we
+ // can't change the NETLOGON_VALIDATION_SAM_INFO2 structure between
+ // releases NT 1.0 and NT 1.0A. HomeDirectoryDrive was NULL for release 1.0A
+ // so we'll use that field.
+ //
+
+ if ( LogonInfo->ParameterControl & MSV1_0_RETURN_USER_PARAMETERS ) {
+ NlpPutString( &ValidationSam->HomeDirectoryDrive,
+ (PUNICODE_STRING)&UserAll->Parameters,
+ &Where );
+ }
+
+
+#if DBG
+ //
+ // Code to test for returning sids
+ //
+
+ //
+ // There are three different tests:
+ //
+ // 1. Some SIDs, some RIDs, and the userid & primary group is a RID
+ //
+ // 2. Some RIDs, some SIDs, and the userid & primary group is a SID
+ //
+ // 3. No RIDs, only SIDs are returned
+ //
+ //
+ // In addition to the real IDs being passed back as SIDs, an additional
+ // SID will be passed back that will be identifiable in pview -
+ // 1-4-9999-9999-9999-9999
+ //
+ // Note: These test cases leak memory - they allocate SIDs and don't
+ // ever free them.
+ //
+
+ if (ValidationLevel == NetlogonValidationSamInfo2)
+ {
+ UNICODE_STRING TestAccountName;
+ ULONG TestLevel = 0;
+ ULONG i,index;
+
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
+
+ RtlInitUnicodeString(&TestAccountName,L"NLTEST_USER_1");
+ if (!RtlCompareUnicodeString(&LogonInfo->UserName,&TestAccountName,TRUE)) {
+ TestLevel = 1;
+ }
+ RtlInitUnicodeString(&TestAccountName,L"NLTEST_USER_2");
+ if (!RtlCompareUnicodeString(&LogonInfo->UserName,&TestAccountName,TRUE)) {
+ TestLevel = 2;
+ }
+ RtlInitUnicodeString(&TestAccountName,L"NLTEST_USER_3");
+ if (!RtlCompareUnicodeString(&LogonInfo->UserName,&TestAccountName,TRUE)) {
+ TestLevel = 3;
+ }
+
+ switch(TestLevel)
+ {
+ case 1:
+
+ //
+ // Just some extra SIDs
+ //
+
+ ValidationSam->SidCount = 2;
+ ValidationSam->ExtraSids = MIDL_user_allocate(ValidationSam->SidCount * sizeof(NETLOGON_SID_AND_ATTRIBUTES));
+
+ ValidationSam->ExtraSids[0].Attributes = 0;
+ ValidationSam->ExtraSids[1].Attributes = 0;
+
+ RtlAllocateAndInitializeSid(
+ &SidAuthority,
+ 5,
+ 9999,
+ 9999,
+ 9999,
+ 9999,
+ 9999,
+ 0,
+ 0,
+ 0,
+ &ValidationSam->ExtraSids[0].Sid );
+
+ RtlAllocateAndInitializeSid(
+ &SidAuthority,
+ 5,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0,
+ 0,
+ 0,
+ &ValidationSam->ExtraSids[1].Sid );
+ break;
+ case 2:
+
+ //
+ // User and primary group are SIDs, some group RIDs, some other
+ // extra SIDs
+ //
+
+ ValidationSam->SidCount = 4;
+ ValidationSam->ExtraSids = MIDL_user_allocate(ValidationSam->SidCount * sizeof(NETLOGON_SID_AND_ATTRIBUTES));
+
+ ValidationSam->ExtraSids[0].Attributes = 0;
+ ValidationSam->ExtraSids[1].Attributes = 7;
+ ValidationSam->ExtraSids[2].Attributes = 0;
+ ValidationSam->ExtraSids[3].Attributes = 0;
+
+ ValidationSam->ExtraSids[0].Sid = NlpMakeDomainRelativeSid(
+ ValidationSam->LogonDomainId,
+ ValidationSam->UserId );
+ ValidationSam->UserId = 0;
+
+ ValidationSam->ExtraSids[1].Sid = NlpMakeDomainRelativeSid(
+ ValidationSam->LogonDomainId,
+ ValidationSam->PrimaryGroupId );
+
+ ValidationSam->PrimaryGroupId = 0;
+
+
+ RtlAllocateAndInitializeSid(
+ &SidAuthority,
+ 5,
+ 9999,
+ 9999,
+ 9999,
+ 9999,
+ 9999,
+ 0,
+ 0,
+ 0,
+ &ValidationSam->ExtraSids[2].Sid );
+
+ RtlAllocateAndInitializeSid(
+ &SidAuthority,
+ 5,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0,
+ 0,
+ 0,
+ &ValidationSam->ExtraSids[3].Sid );
+ break;
+ case 3:
+
+ //
+ // No RIDs, SIDs only
+ //
+
+ index = 4;
+
+ ValidationSam->SidCount = 4 + ValidationSam->GroupCount;
+ ValidationSam->ExtraSids = MIDL_user_allocate(ValidationSam->SidCount * sizeof(NETLOGON_SID_AND_ATTRIBUTES));
+
+ ValidationSam->ExtraSids[0].Attributes = 0;
+ ValidationSam->ExtraSids[1].Attributes = 7;
+ ValidationSam->ExtraSids[2].Attributes = 0;
+ ValidationSam->ExtraSids[3].Attributes = 0;
+
+ ValidationSam->ExtraSids[0].Sid = NlpMakeDomainRelativeSid(
+ ValidationSam->LogonDomainId,
+ ValidationSam->UserId );
+ ValidationSam->UserId = 0;
+
+ ValidationSam->ExtraSids[1].Sid = NlpMakeDomainRelativeSid(
+ ValidationSam->LogonDomainId,
+ ValidationSam->PrimaryGroupId );
+
+ ValidationSam->PrimaryGroupId = 0;
+
+
+ RtlAllocateAndInitializeSid(
+ &SidAuthority,
+ 5,
+ 9999,
+ 9999,
+ 9999,
+ 9999,
+ 9999,
+ 0,
+ 0,
+ 0,
+ &ValidationSam->ExtraSids[2].Sid );
+
+ RtlAllocateAndInitializeSid(
+ &SidAuthority,
+ 5,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0xffff,
+ 0,
+ 0,
+ 0,
+ &ValidationSam->ExtraSids[3].Sid );
+
+ for ( i=0; i < ValidationSam->GroupCount; i++ ) {
+
+ ValidationSam->ExtraSids[index].Attributes =
+ ValidationSam->GroupIds[i].Attributes;
+ ValidationSam->ExtraSids[index].Sid =
+ NlpMakeDomainRelativeSid(
+ ValidationSam->LogonDomainId,
+ ValidationSam->GroupIds[i].RelativeId );
+ index++;
+ }
+
+ ValidationSam->GroupCount = 0;
+ ValidationSam->GroupIds = NULL;
+ break;
+ }
+ }
+#endif
+
+
+ *Authoritative = TRUE;
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup up before returning.
+ //
+
+Cleanup:
+
+ //
+ // If the User Parameters have been changed,
+ // write them back to SAM.
+ //
+
+ if ( NT_SUCCESS(Status) &&
+ (WhichFields & USER_ALL_PARAMETERS) ) {
+ NTSTATUS TempStatus;
+ SAMPR_USER_INFO_BUFFER UserInfo;
+
+ UserInfo.Parameters.Parameters = UserAll->Parameters;
+
+ TempStatus = SamrSetInformationUser( UserHandle,
+ UserParametersInformation,
+ &UserInfo );
+
+ ASSERT(NT_SUCCESS(TempStatus));
+ }
+
+
+ //
+ // Update the logon statistics.
+ //
+
+ if ( NT_SUCCESS(Status) || Status == STATUS_WRONG_PASSWORD ) {
+
+ SAMPR_USER_INFO_BUFFER UserInfo;
+
+ if ( NT_SUCCESS( Status ) ) {
+ if ( LogonLevel == NetlogonInteractiveInformation ) {
+ UserInfo.Internal2.StatisticsToApply =
+ USER_LOGON_INTER_SUCCESS_LOGON;
+ } else {
+
+ //
+ // On network logons,
+ // only update the statistics on 'success' if explicitly asked,
+ // or the Bad Password count will be zeroed.
+ //
+
+ if ( (LogonInfo->ParameterControl & MSV1_0_UPDATE_LOGON_STATISTICS) ||
+ UserAll->BadPasswordCount != 0 ) {
+
+ UserInfo.Internal2.StatisticsToApply =
+ USER_LOGON_NET_SUCCESS_LOGON;
+ } else {
+ UserInfo.Internal2.StatisticsToApply = 0;
+ }
+ }
+
+ // Tell the caller we zeroed the bad password count
+ if ( UserAll->BadPasswordCount != 0 ) {
+ *BadPasswordCountZeroed = TRUE;
+ }
+
+ } else {
+ UserInfo.Internal2.StatisticsToApply =
+ USER_LOGON_BAD_PASSWORD;
+ }
+
+ if ( UserInfo.Internal2.StatisticsToApply != 0 ) {
+ NTSTATUS LogonStatus;
+ LogonStatus = SamrSetInformationUser( UserHandle,
+ UserInternal2Information,
+ &UserInfo );
+ ASSERT(NT_SUCCESS(LogonStatus));
+ }
+
+ }
+
+
+ //
+ // Return the validation buffer to the caller.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ if (ValidationSam != NULL) {
+ MIDL_user_free( ValidationSam );
+ ValidationSam = NULL;
+ }
+ }
+
+ *ValidationInformation = ValidationSam;
+
+ //
+ // Free locally used resources.
+ //
+
+ SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
+ SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
+
+ if ( UserAllInfo != NULL ) {
+ SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
+ }
+
+ if ( GroupsBuffer != NULL ) {
+ SamIFree_SAMPR_GET_GROUPS_BUFFER( GroupsBuffer );
+ }
+
+ if ( UserHandle != NULL ) {
+ SamrCloseHandle( &UserHandle );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+MsvSamValidate (
+ IN SAM_HANDLE DomainHandle,
+ IN BOOLEAN UasCompatibilityRequired,
+ IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
+ IN PUNICODE_STRING LogonServer,
+ IN PUNICODE_STRING LogonDomainName,
+ IN PSID LogonDomainId,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel,
+ OUT PVOID * ValidationInformation,
+ OUT PBOOLEAN Authoritative,
+ OUT PBOOLEAN BadPasswordCountZeroed,
+ IN DWORD AccountsToTry
+)
+/*++
+
+Routine Description:
+
+ Process an interactive, network, or session logon. It calls
+ SamIUserValidation, validates the passed in credentials, updates the logon
+ statistics and packages the result for return to the caller.
+
+ This routine is called directly from the MSV Authentication package
+ if the account is defined locally. This routine is called
+ from the Netlogon Service otherwise.
+
+Arguments:
+
+ DomainHandle -- Specifies a handle to the SamDomain to use to
+ validate the request.
+
+ UasCompatibilityRequired -- TRUE iff UasCompatibilityRequired is on.
+
+ SecureChannelType -- The secure channel type this request was made on.
+
+ LogonServer -- Specifies the server name of the caller.
+
+ LogonDomainName -- Specifies the domain of the caller.
+
+ LogonDomainId -- Specifies the DomainId of the domain of the caller.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on. The LogonDomainName field should be ignored.
+ The caller is responsible for validating this field.
+
+ ValidationLevel -- Specifies the level of information returned in
+ ValidationInformation. Must be NetlogonValidationSamInfo or
+ NetlogonValidationSamInfo2.
+
+ ValidationInformation -- Returns the requested validation
+ information. This buffer must be freed user MIDL_user_free.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+ BadPasswordCountZeroed - Returns TRUE iff we zeroed the BadPasswordCount
+ field of this user.
+
+ AccountsToTry -- Specifies whether the username specified in
+ LogonInformation is to be used to logon, whether to guest account
+ is to be used to logon, or both serially.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+ STATUS_INVALID_INFO_CLASS: LogonLevel or ValidationLevel are invalid.
+ STATUS_NO_SUCH_USER: The specified user has no account.
+ STATUS_WRONG_PASSWORD: The password was invalid.
+
+ Other return codes from SamIUserValidation
+
+--*/
+{
+ NTSTATUS Status;
+ NTSTATUS GuestStatus;
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+
+
+ //
+ // Validate the specified user.
+ //
+ *BadPasswordCountZeroed = FALSE;
+
+ if ( AccountsToTry & MSVSAM_SPECIFIED ) {
+
+ //
+ // Keep track of the total number of logons attempted.
+ //
+
+ RtlEnterCriticalSection(&NlpSessionCountLock);
+ NlpLogonAttemptCount ++;
+ RtlLeaveCriticalSection(&NlpSessionCountLock);
+
+ Status = MsvpSamValidate( (SAMPR_HANDLE) DomainHandle,
+ UasCompatibilityRequired,
+ SecureChannelType,
+ LogonServer,
+ LogonDomainName,
+ LogonDomainId,
+ LogonLevel,
+ LogonInformation,
+ 0,
+ ValidationLevel,
+ ValidationInformation,
+ Authoritative,
+ BadPasswordCountZeroed );
+
+ //
+ // If the SAM database authoritatively handled this logon attempt,
+ // just return.
+ //
+
+ if ( *Authoritative ) {
+ return Status;
+ }
+
+ //
+ // If the caller only wants to log on as guest,
+ // Pretend the first validation simply didn't find the user.
+ //
+ } else {
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+
+ }
+
+ //
+ // If guest accounts are not allowed,
+ // return now.
+ //
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
+
+ if ( LogonLevel != NetlogonNetworkInformation ||
+ SecureChannelType != MsvApSecureChannel ||
+ ( LogonInfo->ParameterControl & MSV1_0_DONT_TRY_GUEST_ACCOUNT ) ||
+ (AccountsToTry & MSVSAM_GUEST) == 0 ) {
+
+ return Status;
+ }
+
+ //
+ // Try the Guest Account.
+ //
+
+ GuestStatus = MsvpSamValidate( (SAMPR_HANDLE) DomainHandle,
+ UasCompatibilityRequired,
+ SecureChannelType,
+ LogonServer,
+ LogonDomainName,
+ LogonDomainId,
+ LogonLevel,
+ LogonInformation,
+ DOMAIN_USER_RID_GUEST,
+ ValidationLevel,
+ ValidationInformation,
+ Authoritative,
+ BadPasswordCountZeroed );
+
+ if ( NT_SUCCESS(GuestStatus) ) {
+ PNETLOGON_VALIDATION_SAM_INFO2 ValidationInfo;
+
+ ASSERT ((ValidationLevel == NetlogonValidationSamInfo) ||
+ (ValidationLevel == NetlogonValidationSamInfo2) );
+ ValidationInfo =
+ (PNETLOGON_VALIDATION_SAM_INFO2) *ValidationInformation;
+ ValidationInfo->UserFlags |= LOGON_GUEST;
+ return GuestStatus;
+ }
+
+ //
+ // Failed Guest logon attempts are never authoritative and the status from
+ // the original logon attempt is more significant than the Guest logon
+ // status.
+ //
+ *Authoritative = FALSE;
+ return Status;
+
+}
+
+
+NTSTATUS
+MsvSamLogoff (
+ IN SAM_HANDLE DomainHandle,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation
+)
+/*++
+
+Routine Description:
+
+ Process an interactive, network, or session logoff. It simply updates
+ the logon statistics for the user account.
+
+ This routine is called directly from the MSV Authentication package
+ if the user was logged on not using the Netlogon service. This routine
+ is called from the Netlogon Service otherwise.
+
+Arguments:
+
+ DomainHandle -- Specifies a handle to the SamDomain containing
+ the user to logoff.
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on. The LogonDomainName field should be ignored.
+ The caller is responsible for validating this field.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+ STATUS_INVALID_INFO_CLASS: LogonLevel or ValidationLevel are invalid.
+ STATUS_NO_SUCH_USER: The specified user has no account.
+ STATUS_WRONG_PASSWORD: The password was invalid.
+
+ Other return codes from SamIUserValidation
+
+--*/
+{
+ NTSTATUS Status;
+
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+
+ SAMPR_HANDLE UserHandle = NULL;
+ SAMPR_USER_INFO_BUFFER UserInfo;
+
+ SAMPR_ULONG_ARRAY RelativeIdArray;
+ SAMPR_ULONG_ARRAY UseArray;
+
+
+ //
+ // Initialization
+ //
+
+ RelativeIdArray.Count = 1;
+ RelativeIdArray.Element = NULL;
+ UseArray.Count = 1;
+ UseArray.Element = NULL;
+
+ //
+ // Only update logoff stats for interactive logoff.
+ //
+
+ if ( LogonLevel != NetlogonInteractiveInformation ) {
+ return STATUS_SUCCESS;
+ }
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
+
+
+ //
+ // Convert the user name to a RelativeId.
+ //
+
+ Status = SamrLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ (PRPC_UNICODE_STRING) &LogonInfo->UserName,
+ &RelativeIdArray,
+ &UseArray );
+
+ if ( !NT_SUCCESS(Status) ) {
+ RelativeIdArray.Element = NULL;
+ UseArray.Element = NULL;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+ if ( UseArray.Element[0] != SidTypeUser ) {
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Open the user account.
+ //
+
+ Status = SamrOpenUser( DomainHandle,
+ USER_READ_GENERAL | USER_READ_PREFERENCES |
+ USER_READ_LOGON,
+ RelativeIdArray.Element[0],
+ &UserHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ UserHandle = NULL;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Update the logon statistics.
+ //
+
+ UserInfo.Internal2.StatisticsToApply = USER_LOGON_INTER_SUCCESS_LOGOFF;
+
+ Status = SamrSetInformationUser( UserHandle,
+ UserInternal2Information,
+ &UserInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup up before returning.
+ //
+Cleanup:
+
+ //
+ // Free locally used resources.
+ //
+ SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
+ SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
+
+ if ( UserHandle != NULL ) {
+ SamrCloseHandle( &UserHandle );
+ }
+
+ return Status;
+}
+
+
+ULONG
+MsvGetLogonAttemptCount (
+ VOID
+)
+/*++
+
+Routine Description:
+
+ Return the number of logon attempts since the last reboot.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ Returns the number of logon attempts since the last reboot.
+
+--*/
+{
+ ULONG LogonAttemptCount;
+
+ //
+ // Keep track of the total number of logons attempted.
+ //
+
+ RtlEnterCriticalSection(&NlpSessionCountLock);
+ LogonAttemptCount = NlpLogonAttemptCount;
+ RtlLeaveCriticalSection(&NlpSessionCountLock);
+
+ return LogonAttemptCount;
+}
diff --git a/private/lsa/msv1_0/nlmain.c b/private/lsa/msv1_0/nlmain.c
new file mode 100644
index 000000000..4ec37429a
--- /dev/null
+++ b/private/lsa/msv1_0/nlmain.c
@@ -0,0 +1,3303 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ nlmain.c
+
+Abstract:
+
+ This file contains the initialization and dispatch routines
+ for the LAN Manager portions of the MSV1_0 authentication package.
+
+Author:
+
+ Jim Kelly 11-Apr-1991
+
+Revision History:
+
+ 25-Apr-1991 (cliffv)
+ Added interactive logon support for PDK.
+
+--*/
+
+
+#include "msp.h"
+#define NLP_ALLOCATE
+#include "nlp.h"
+#undef NLP_ALLOCATE
+#include <stdlib.h>
+#include <rpc.h> // Needed by samrpc.h
+#include <samisrv.h> // SamIFree Routines
+#include <lsarpc.h> // Lsar routines
+#include <lsaisrv.h> // LsaIFree and Trusted Client Routines
+
+#include <lmcons.h>
+#include <lmerr.h>
+#include <lmapibuf.h> // NetApiBufferFree
+#include <lmaccess.h> // NetGetDCName
+#include <lmremutl.h> // NetRemoteComputerSupports
+#include <lmsname.h> // Service Names
+#include <winsvc.h> // Service Controller
+
+#include "nlpcache.h" // logon cache prototypes
+
+
+NTSTATUS
+NlInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize NETLOGON portion of msv1_0 authentication package.
+
+Arguments:
+
+ None.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LPWSTR ComputerName;
+ DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
+ NT_PRODUCT_TYPE NtProductType;
+ UNICODE_STRING TempUnicodeString;
+
+ //
+ // Initialize global data
+ //
+
+ NlpEnumerationHandle = 0;
+ NlpSessionCount = 0;
+ NlpLogonAttemptCount = 0;
+
+
+ NlpComputerName.Buffer = NULL;
+ NlpSamDomainName.Buffer = NULL;
+ NlpSamDomainId = NULL;
+ NlpSamDomainHandle = NULL;
+
+
+
+ //
+ // Get the name of this machine.
+ //
+
+ ComputerName = RtlAllocateHeap(
+ MspHeap, 0,
+ ComputerNameLength * sizeof(WCHAR) );
+
+ if (ComputerName == NULL ||
+ !GetComputerNameW( ComputerName, &ComputerNameLength )) {
+
+ KdPrint(( "MsV1_0: Cannot get computername %lX\n", GetLastError() ));
+
+ NlpLanmanInstalled = FALSE;
+ RtlFreeHeap( MspHeap, 0, ComputerName );
+ ComputerName = NULL;
+ } else {
+
+ NlpLanmanInstalled = TRUE;
+ }
+
+ RtlInitUnicodeString( &NlpComputerName, ComputerName );
+
+ //
+ // Determine if this machine is running Windows NT or Lanman NT.
+ // LanMan NT runs on a domain controller.
+ //
+
+ if ( !RtlGetNtProductType( &NtProductType ) ) {
+ KdPrint(( "MsV1_0: Nt Product Type undefined (WinNt assumed)\n" ));
+ NtProductType = NtProductWinNt;
+ }
+
+ NlpWorkstation = (BOOLEAN)(NtProductType != NtProductLanManNt);
+
+
+
+ //
+ // Initialize any locks.
+ //
+
+ RtlInitializeCriticalSection(&NlpActiveLogonLock);
+ RtlInitializeCriticalSection(&NlpSessionCountLock);
+
+ //
+ // initialize the cache - creates a critical section is all
+ //
+
+ NlpCacheInitialize();
+
+
+ //
+ // Attempt to load Netlogon.dll
+ //
+
+ NlpLoadNetlogonDll();
+
+#ifdef COMPILED_BY_DEVELOPER
+ KdPrint(("msv1_0: COMPILED_BY_DEVELOPER breakpoint.\n"));
+ DbgBreakPoint();
+#endif // COMPILED_BY_DEVELOPER
+
+
+
+ //
+ // Initialize useful encryption constants
+ //
+
+ Status = RtlCalculateLmOwfPassword( "", &NlpNullLmOwfPassword );
+ ASSERT( NT_SUCCESS(Status) );
+
+ RtlInitUnicodeString(&TempUnicodeString, NULL);
+ Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
+ &NlpNullNtOwfPassword);
+ ASSERT( NT_SUCCESS(Status) );
+
+ //
+ // Initialize the SubAuthentication Dlls
+ //
+
+ Msv1_0SubAuthenticationInitialization();
+
+
+
+
+#ifdef notdef
+ //
+ // If we weren't successful,
+ // Clean up global resources we intended to initialize.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ if ( NlpComputerName.Buffer != NULL ) {
+ MIDL_user_free( NlpComputerName.Buffer );
+ }
+
+ }
+#endif // notdef
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+NlWaitForEvent(
+ LPWSTR EventName,
+ ULONG Timeout
+ )
+
+/*++
+
+Routine Description:
+
+ Wait up to Timeout seconds for EventName to be triggered.
+
+Arguments:
+
+ EventName - Name of event to wait on
+
+ Timeout - Timeout for event (in seconds).
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
+ STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ HANDLE EventHandle;
+ OBJECT_ATTRIBUTES EventAttributes;
+ UNICODE_STRING EventNameString;
+ LARGE_INTEGER LocalTimeout;
+
+
+ //
+ // Create an event for us to wait on.
+ //
+
+ RtlInitUnicodeString( &EventNameString, EventName);
+ InitializeObjectAttributes( &EventAttributes, &EventNameString, 0, 0, NULL);
+
+ Status = NtCreateEvent(
+ &EventHandle,
+ SYNCHRONIZE,
+ &EventAttributes,
+ NotificationEvent,
+ (BOOLEAN) FALSE // The event is initially not signaled
+ );
+
+ if ( !NT_SUCCESS(Status)) {
+
+ //
+ // If the event already exists, the server beat us to creating it.
+ // Just open it.
+ //
+
+ if( Status == STATUS_OBJECT_NAME_EXISTS ||
+ Status == STATUS_OBJECT_NAME_COLLISION ) {
+
+ Status = NtOpenEvent( &EventHandle,
+ SYNCHRONIZE,
+ &EventAttributes );
+
+ }
+ if ( !NT_SUCCESS(Status)) {
+ KdPrint(("[MSV1_0] OpenEvent failed %lx\n", Status ));
+ return Status;
+ }
+ }
+
+
+ //
+ // Wait for NETLOGON to initialize. Wait a maximum of Timeout seconds.
+ //
+
+ LocalTimeout.QuadPart = ((LONGLONG)(Timeout)) * (-10000000);
+ Status = NtWaitForSingleObject( EventHandle, (BOOLEAN)FALSE, &LocalTimeout);
+ (VOID) NtClose( EventHandle );
+
+ if ( !NT_SUCCESS(Status) || Status == STATUS_TIMEOUT ) {
+ if ( Status == STATUS_TIMEOUT ) {
+ Status = STATUS_NETLOGON_NOT_STARTED; // Map to an error condition
+ }
+ return Status;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NlWaitForNetlogon(
+ ULONG Timeout
+ )
+
+/*++
+
+Routine Description:
+
+ Wait up to Timeout seconds for the netlogon service to start.
+
+Arguments:
+
+ Timeout - Timeout for event (in seconds).
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
+ STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
+
+--*/
+
+{
+ NTSTATUS Status;
+ NET_API_STATUS NetStatus;
+ SC_HANDLE ScManagerHandle = NULL;
+ SC_HANDLE ServiceHandle = NULL;
+ SERVICE_STATUS ServiceStatus;
+ LPQUERY_SERVICE_CONFIG ServiceConfig;
+ LPQUERY_SERVICE_CONFIG AllocServiceConfig = NULL;
+ QUERY_SERVICE_CONFIG DummyServiceConfig;
+ DWORD ServiceConfigSize;
+
+
+ //
+ // If the netlogon service is currently running,
+ // skip the rest of the tests.
+ //
+
+ Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 0 );
+
+ if ( NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+
+ //
+ // Open a handle to the Netlogon Service.
+ //
+
+ ScManagerHandle = OpenSCManager(
+ NULL,
+ NULL,
+ SC_MANAGER_CONNECT );
+
+ if (ScManagerHandle == NULL) {
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: OpenSCManager failed: "
+ "%lu\n", GetLastError()));
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+ ServiceHandle = OpenService(
+ ScManagerHandle,
+ SERVICE_NETLOGON,
+ SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
+
+ if ( ServiceHandle == NULL ) {
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: OpenService failed: "
+ "%lu\n", GetLastError()));
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+
+ //
+ // If the Netlogon service isn't configured to be automatically started
+ // by the service controller, don't bother waiting for it to start.
+ //
+ // ?? Pass "DummyServiceConfig" and "sizeof(..)" since QueryService config
+ // won't allow a null pointer, yet.
+
+ if ( QueryServiceConfig(
+ ServiceHandle,
+ &DummyServiceConfig,
+ sizeof(DummyServiceConfig),
+ &ServiceConfigSize )) {
+
+ ServiceConfig = &DummyServiceConfig;
+
+ } else {
+
+ NetStatus = GetLastError();
+ if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) {
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: QueryServiceConfig failed: "
+ "%lu\n", NetStatus));
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+ AllocServiceConfig = RtlAllocateHeap( MspHeap, 0, ServiceConfigSize );
+ ServiceConfig = AllocServiceConfig;
+
+ if ( AllocServiceConfig == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ if ( !QueryServiceConfig(
+ ServiceHandle,
+ ServiceConfig,
+ ServiceConfigSize,
+ &ServiceConfigSize )) {
+
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: QueryServiceConfig "
+ "failed again: %lu\n", GetLastError()));
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+ }
+
+ if ( ServiceConfig->dwStartType != SERVICE_AUTO_START ) {
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: Netlogon start type invalid:"
+ "%lu\n", ServiceConfig->dwStartType ));
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Loop waiting for the netlogon service to start.
+ //
+
+ for (;;) {
+
+
+ //
+ // Query the status of the Netlogon service.
+ //
+
+ if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
+
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: QueryServiceStatus failed: "
+ "%lu\n", GetLastError() ));
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+ //
+ // Return or continue waiting depending on the state of
+ // the netlogon service.
+ //
+
+ switch( ServiceStatus.dwCurrentState) {
+ case SERVICE_RUNNING:
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+
+ case SERVICE_STOPPED:
+
+ //
+ // If Netlogon failed to start,
+ // error out now. The caller has waited long enough to start.
+ //
+ if ( ServiceStatus.dwWin32ExitCode != ERROR_SERVICE_NEVER_STARTED ){
+#if DBG
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: "
+ "Netlogon service couldn't start: %lu %lx\n",
+ ServiceStatus.dwWin32ExitCode,
+ ServiceStatus.dwWin32ExitCode ));
+ if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) {
+ KdPrint(( " Service specific error code: %lu %lx\n",
+ ServiceStatus.dwServiceSpecificExitCode,
+ ServiceStatus.dwServiceSpecificExitCode ));
+ }
+#endif // DBG
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+ //
+ // If Netlogon has never been started on this boot,
+ // continue waiting for it to start.
+ //
+
+ break;
+
+ //
+ // If Netlogon is trying to start up now,
+ // continue waiting for it to start.
+ //
+ case SERVICE_START_PENDING:
+ break;
+
+ //
+ // Any other state is bogus.
+ //
+ default:
+ KdPrint(( "[MSV1_0] NlWaitForNetlogon: "
+ "Invalid service state: %lu\n",
+ ServiceStatus.dwCurrentState ));
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+
+ }
+
+
+ //
+ // Wait a second for the netlogon service to start.
+ // If it has successfully started, just return now.
+ //
+
+ Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 1 );
+
+ if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
+ goto Cleanup;
+ }
+
+ //
+ // If we've waited long enough for netlogon to start,
+ // time out now.
+ //
+
+ if ( (--Timeout) == 0 ) {
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ goto Cleanup;
+ }
+
+
+ }
+
+ /* NOT REACHED */
+
+Cleanup:
+ if ( ScManagerHandle != NULL ) {
+ (VOID) CloseServiceHandle(ScManagerHandle);
+ }
+ if ( ServiceHandle != NULL ) {
+ (VOID) CloseServiceHandle(ServiceHandle);
+ }
+ if ( AllocServiceConfig != NULL ) {
+ RtlFreeHeap( MspHeap, 0, AllocServiceConfig );
+ }
+ return Status;
+}
+
+
+NTSTATUS
+NlSamInitialize(
+ ULONG Timeout
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize the MSV1_0 Authentication Package's communication to the SAM
+ database. This initialization will take place once immediately prior
+ to the first actual use of the SAM database.
+
+Arguments:
+
+ Timeout - Timeout for event (in seconds).
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSA_HANDLE PolicyHandle = NULL;
+ OBJECT_ATTRIBUTES PolicyObjectAttributes;
+ PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
+
+ SAMPR_HANDLE SamHandle = NULL;
+#ifdef SAM
+ PSAMPR_DOMAIN_INFO_BUFFER DomainInfo = NULL;
+#endif // SAM
+
+
+ //
+ // Wait for SAM to finish initialization.
+ //
+
+ Status = NlWaitForEvent( L"\\SAM_SERVICE_STARTED", Timeout );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Determine the DomainName and DomainId of the Account Database
+ //
+
+ InitializeObjectAttributes( &PolicyObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ Status = LsaIOpenPolicyTrusted(&NlpPolicyHandle);
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ Status = LsarQueryInformationPolicy( NlpPolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ||
+ PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length == 0 ) {
+ KdPrint(( "MsV1_0: Account domain info from LSA invalid.\n"));
+ Status = STATUS_NO_SUCH_DOMAIN;
+ goto Cleanup;
+ }
+
+ //
+ // Save the domain id of this domain
+ //
+
+ NlpSamDomainId = RtlAllocateHeap(
+ MspHeap, 0,
+ RtlLengthSid( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid ));
+
+ if ( NlpSamDomainId == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( NlpSamDomainId,
+ PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
+ RtlLengthSid( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid ));
+
+
+ //
+ // Save the name of the account database on this machine.
+ //
+ // On a workstation, the account database is refered to by the machine
+ // name and not the database name.
+ //
+
+ if ( NlpWorkstation ) {
+ NlpSamDomainName = NlpComputerName;
+ } else {
+
+ NlpSamDomainName.Length = PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length;
+ NlpSamDomainName.MaximumLength = (USHORT)
+ (NlpSamDomainName.Length + sizeof(WCHAR));
+
+ NlpSamDomainName.Buffer =
+ RtlAllocateHeap( MspHeap, 0, NlpSamDomainName.MaximumLength );
+
+ if ( NlpSamDomainName.Buffer == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory( NlpSamDomainName.Buffer,
+ PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Buffer,
+ NlpSamDomainName.MaximumLength );
+
+ }
+
+ //
+ // Open our connection with SAM
+ //
+
+ Status = SamIConnect( NULL, // No server name
+ &SamHandle,
+ SAM_SERVER_CONNECT,
+ (BOOLEAN) TRUE ); // Indicate we are privileged
+
+ if ( !NT_SUCCESS(Status) ) {
+ SamHandle = NULL;
+ KdPrint(( "MsV1_0: Cannot SamIConnect %lX\n", Status));
+ goto Cleanup;
+ }
+
+ //
+ // Open the domain.
+ //
+
+ Status = SamrOpenDomain( SamHandle,
+ DOMAIN_ALL_ACCESS,
+ NlpSamDomainId,
+ &NlpSamDomainHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlpSamDomainHandle = NULL;
+ KdPrint(( "MsV1_0: Cannot SamrOpenDomain %lX\n", Status));
+ goto Cleanup;
+ }
+
+#ifdef SAM
+ //
+ // Ensure the role in SAM is compatible with Netlogon's role
+ // ?? Use DomainUasInformation once it is defined
+ //
+
+ Status = SamrQueryInformationDomain( NlpSamDomainHandle,
+ DomainGeneralInformation,
+ &DomainInfo );
+ if ( !NT_SUCCESS(Status) ) {
+ DomainInfo = NULL;
+ KdPrint(( "MsV1_0: Cannot SamrQueryInformationDomain %lX\n", Status));
+ goto Cleanup;
+ }
+
+ // ?? Doesn't define this properly
+ NlpUasCompatibilityRequired = DomainInfo->General.UasCompatibilityRequired;
+
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainInfo, DomainGeneralInformation );
+#endif // SAM
+
+
+
+ Status = STATUS_SUCCESS;
+ NlpSamInitialized = TRUE;
+
+
+
+Cleanup:
+ //
+ // If we weren't successful,
+ // Clean up global resources we intended to initialize.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if ( !NlpWorkstation ) {
+ if ( NlpSamDomainName.Buffer != NULL ) {
+ RtlFreeHeap( MspHeap, 0, NlpSamDomainName.Buffer );
+ }
+ }
+
+ if ( NlpSamDomainHandle != NULL ) {
+ (VOID) SamrCloseHandle( &NlpSamDomainHandle );
+ }
+
+ if ( NlpSamDomainId != NULL ) {
+ RtlFreeHeap( MspHeap, 0, NlpSamDomainId );
+ }
+
+ if (NlpPolicyHandle != NULL) {
+
+ (VOID) LsaClose( NlpPolicyHandle );
+ NlpPolicyHandle = NULL;
+ }
+ }
+
+ //
+ // Free locally used resources.
+ //
+
+ if ( PolicyAccountDomainInfo != NULL ) {
+ LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAccountDomainInformation,
+ PolicyAccountDomainInfo );
+ }
+
+ if ( SamHandle != NULL ) {
+ (VOID) SamrCloseHandle( &SamHandle );
+ }
+
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+MspLm20Challenge (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of MsV1_0Lm20ChallengeRequest. It is called by
+ the LanMan server to determine the Challenge to pass back to a
+ redirector trying to establish a connection to the server. The server
+ is responsible remembering this Challenge and passing in back to this
+ authentication package on a subsequent MsV1_0Lm20Logon request.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PMSV1_0_LM20_CHALLENGE_REQUEST ChallengeRequest;
+ PMSV1_0_LM20_CHALLENGE_RESPONSE ChallengeResponse;
+ CLIENT_BUFFER_DESC ClientBufferDesc;
+
+ ULONG Seed[2];
+ CYPHER_BLOCK Challenge;
+
+ UNREFERENCED_PARAMETER( ClientBufferBase );
+
+ ASSERT( sizeof(LM_CHALLENGE) == MSV1_0_CHALLENGE_LENGTH );
+ ASSERT( sizeof(LM_CHALLENGE) == sizeof(CYPHER_BLOCK) );
+ NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size and
+ // relocate all of the pointers to be relative to the LSA allocated
+ // buffer.
+ //
+
+ if ( SubmitBufferSize < sizeof(MSV1_0_LM20_CHALLENGE_REQUEST) ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ ChallengeRequest = (PMSV1_0_LM20_CHALLENGE_REQUEST) ProtocolSubmitBuffer;
+
+ ASSERT( ChallengeRequest->MessageType == MsV1_0Lm20ChallengeRequest );
+
+ //
+ // Build an 8-byte challenge as follows:
+ //
+ // Take the 8-byte time of day.
+ // Add a monotonically increasing value to ensure no two times are the same
+ // Use that to OWF encrypt the key.
+ //
+
+
+ //
+ // Compute a relatively random seed.
+ //
+
+#ifdef USE_CONSTANT_CHALLENGE
+ RtlZeroMemory( &Challenge, sizeof(Challenge) );
+#else // USE_CONSTANT_CHALLENGE
+ RtlZeroMemory(Seed, sizeof(Seed));
+
+ ASSERT( sizeof(LARGE_INTEGER) <= sizeof(Seed) );
+ Status = NtQuerySystemTime ( (PLARGE_INTEGER) Seed );
+ ASSERT( NT_SUCCESS(Status) );
+
+ RtlEnterCriticalSection(&NlpSessionCountLock);
+ Seed[1] = ++NlpSessionCount;
+ RtlLeaveCriticalSection(&NlpSessionCountLock);
+
+ //
+ // Encrypt it to obfuscate it a little more.
+ //
+
+ ASSERT( sizeof(BLOCK_KEY) <= sizeof(Seed) );
+ Status = RtlEncryptStdBlock( (PBLOCK_KEY) Seed, &Challenge );
+ ASSERT( NT_SUCCESS(Status) );
+#endif // USE_CONSTANT_CHALLENGE
+
+ //
+ // Allocate a buffer to return to the caller.
+ //
+
+ *ReturnBufferSize = sizeof(MSV1_0_LM20_CHALLENGE_RESPONSE);
+
+ Status = NlpAllocateClientBuffer( &ClientBufferDesc,
+ sizeof(MSV1_0_LM20_CHALLENGE_RESPONSE),
+ *ReturnBufferSize );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ ChallengeResponse = (PMSV1_0_LM20_CHALLENGE_RESPONSE) ClientBufferDesc.MsvBuffer;
+
+ //
+ // Fill in the return buffer.
+ //
+
+ ChallengeResponse->MessageType = MsV1_0Lm20ChallengeRequest;
+ RtlCopyMemory( ChallengeResponse->ChallengeToClient,
+ &Challenge,
+ sizeof(Challenge) );
+
+ //
+ // Flush the buffer to the client's address space.
+ //
+
+ Status = NlpFlushClientBuffer( &ClientBufferDesc,
+ ProtocolReturnBuffer );
+
+
+Cleanup:
+
+ //
+ // If we weren't successful, free the buffer in the clients address space.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlpFreeClientBuffer( &ClientBufferDesc );
+ }
+
+ //
+ // Return status to the caller.
+ //
+
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+MspLm20GetChallengeResponse (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of MsV1_0Lm20GetChallengeResponse. It is called by
+ the LanMan redirector to determine the Challenge Response to pass to a
+ server when trying to establish a connection to the server.
+
+ This routine is passed a Challenge from the server. This routine encrypts
+ the challenge with either the specified password or with the password
+ implied by the specified Logon Id.
+
+ Two Challenge responses are returned. One is based on the Unicode password
+ as given to the Authentication package. The other is based on that
+ password converted to a multi-byte character set (e.g., ASCII) and upper
+ cased. The redirector should use whichever (or both) challenge responses
+ as it needs them.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PMSV1_0_GETCHALLENRESP_REQUEST GetRespRequest;
+
+ CLIENT_BUFFER_DESC ClientBufferDesc;
+ PMSV1_0_GETCHALLENRESP_RESPONSE GetRespResponse;
+
+ PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
+ PMSV1_0_PRIMARY_CREDENTIAL PrimaryCredential = NULL;
+ PMSV1_0_PRIMARY_CREDENTIAL BuiltCredential = NULL;
+
+ //
+ // Responses to return to the caller.
+ //
+ LM_RESPONSE LmResponse;
+ STRING LmResponseString;
+
+ NT_RESPONSE NtResponse;
+ STRING NtResponseString;
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING LogonDomainName;
+ UNICODE_STRING NullUnicodeString;
+ USER_SESSION_KEY UserSessionKey;
+ UCHAR LanmanSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
+
+ //
+ // Initialization
+ //
+
+ NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
+
+ RtlInitUnicodeString( &UserName, NULL );
+ RtlInitUnicodeString( &LogonDomainName, NULL );
+ RtlInitUnicodeString( &NullUnicodeString, NULL );
+
+ RtlZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
+ RtlZeroMemory( LanmanSessionKey, sizeof(LanmanSessionKey) );
+
+ //
+ // If no credentials are associated with the client, a null session
+ // will be used. For a downlevel server, the null session response is
+ // a 1-byte null string (\0). Initialize LmResponseString to the
+ // null session response.
+ //
+
+ RtlInitString( &LmResponseString, "" );
+ LmResponseString.Length = 1;
+
+ //
+ // Initialize the NT response to the NT null session credentials,
+ // which are zero length.
+ //
+
+ RtlInitString( &NtResponseString, NULL );
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size and
+ // relocate all of the pointers to be relative to the LSA allocated
+ // buffer.
+ //
+
+ if ( SubmitBufferSize < sizeof(MSV1_0_GETCHALLENRESP_REQUEST) ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ GetRespRequest = (PMSV1_0_GETCHALLENRESP_REQUEST) ProtocolSubmitBuffer;
+
+ ASSERT( GetRespRequest->MessageType == MsV1_0Lm20GetChallengeResponse );
+
+ if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) == 0 ) {
+ RELOCATE_ONE( &GetRespRequest->Password );
+ }
+
+
+
+ //
+ // If the caller wants information from the credentials of a specified
+ // LogonId, get those credentials from the LSA.
+ //
+ // If there are no such credentials,
+ // tell the caller to use the NULL session.
+ //
+
+#define PRIMARY_CREDENTIAL_NEEDED \
+ (RETURN_PRIMARY_LOGON_DOMAINNAME | \
+ RETURN_PRIMARY_USERNAME | \
+ USE_PRIMARY_PASSWORD )
+
+ if ( (GetRespRequest->ParameterControl & PRIMARY_CREDENTIAL_NEEDED) != 0 ) {
+
+ Status = NlpGetPrimaryCredential(
+ &GetRespRequest->LogonId,
+ &PrimaryCredential,
+ NULL );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ if ( GetRespRequest->ParameterControl & RETURN_PRIMARY_USERNAME ) {
+ UserName = PrimaryCredential->UserName;
+ }
+
+ if ( GetRespRequest->ParameterControl &
+ RETURN_PRIMARY_LOGON_DOMAINNAME ) {
+ LogonDomainName = PrimaryCredential->LogonDomainName;
+ }
+#ifdef USE_REDMOND_CLIFFV
+ //
+ // Pretend CliffVDom\CliffV is really Redmond\CliffV
+ //
+ // This kludge is required since CliffVDom is not politically allowed
+ // to trust the Redmond domain, but all my resources outside the domain
+ // are owned by Redmond\CliffV.
+ //
+ {
+ UNICODE_STRING Cliffv;
+ UNICODE_STRING CliffvDom;
+
+ RtlInitUnicodeString( &Cliffv, L"CliffV" );
+ RtlInitUnicodeString( &CliffvDom, L"CliffVDom" );
+
+ if ( RtlEqualUnicodeString( &UserName, &Cliffv, TRUE ) &&
+ RtlEqualUnicodeString( &LogonDomainName, &CliffvDom, TRUE ) ) {
+ RtlInitUnicodeString( &LogonDomainName, L"Redmond" );
+ }
+ }
+#endif // USE_REDMOND_CLIFFV
+
+ } else if ( Status == STATUS_NO_SUCH_LOGON_SESSION ||
+ Status == STATUS_UNSUCCESSFUL ) {
+
+ //
+ // Clean up the status code
+ //
+
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+
+ //
+ // If the caller wants at least the password from the primary
+ // credential, just use a NULL session primary credential.
+ //
+
+ if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD ) ==
+ USE_PRIMARY_PASSWORD ) {
+
+ PrimaryCredential = NULL;
+
+ //
+ // If part of the information was supplied by the caller,
+ // report the error to the caller.
+ //
+ } else {
+ KdPrint(("MSV1_0: MspLm20GetChallengeResponse: cannot "
+ " GetPrimaryCredential %lx\n", Status ));
+ goto Cleanup;
+ }
+ } else {
+ KdPrint(("MSV1_0: MspLm20GetChallengeResponse: cannot "
+ " GetPrimaryCredential %lx\n", Status ));
+ goto Cleanup;
+ }
+
+ Credential = PrimaryCredential;
+
+ }
+
+
+ //
+ // If the caller passed in a password to use,
+ // use it to build a credential.
+ //
+
+ if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) == 0 ) {
+ ULONG CredentialSize;
+
+ Status = NlpMakePrimaryCredential( &NullUnicodeString,
+ &NullUnicodeString,
+ &GetRespRequest->Password,
+ &BuiltCredential,
+ &CredentialSize );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Use the newly allocated credential to get the password information
+ // from.
+ //
+ Credential = BuiltCredential;
+
+ }
+
+ //
+ // Build the appropriate response.
+ //
+
+ if ( Credential != NULL ) {
+
+ Status = RtlCalculateLmResponse(
+ (PLM_CHALLENGE) GetRespRequest->ChallengeToClient,
+ &Credential->LmOwfPassword,
+ &LmResponse );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ LmResponseString.Buffer = (PUCHAR) &LmResponse;
+ LmResponseString.Length = sizeof(LmResponse);
+
+ Status = RtlCalculateNtResponse(
+ (PNT_CHALLENGE) GetRespRequest->ChallengeToClient,
+ &Credential->NtOwfPassword,
+ &NtResponse );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ NtResponseString.Buffer = (PUCHAR) &NtResponse;
+ NtResponseString.Length = sizeof(NtResponse);
+
+
+
+ //
+ // Compute the session keys
+ //
+
+ if ( GetRespRequest->ParameterControl & RETURN_NON_NT_USER_SESSION_KEY){
+
+ //
+ // If the redir didn't negotiate an NT protocol with the server,
+ // use the lanman session key.
+ //
+
+ if ( Credential->LmPasswordPresent ) {
+
+ ASSERT( sizeof(UserSessionKey) >= sizeof(LanmanSessionKey) );
+
+ RtlCopyMemory( &UserSessionKey,
+ &Credential->LmOwfPassword,
+ sizeof(LanmanSessionKey) );
+ }
+
+ } else {
+
+ if ( !Credential->NtPasswordPresent ) {
+
+ RtlCopyMemory( &Credential->NtOwfPassword,
+ &NlpNullNtOwfPassword,
+ sizeof(Credential->NtOwfPassword) );
+ }
+
+ Status = RtlCalculateUserSessionKeyNt(
+ &NtResponse,
+ &Credential->NtOwfPassword,
+ &UserSessionKey );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+ }
+
+ if ( Credential->LmPasswordPresent ) {
+ RtlCopyMemory( LanmanSessionKey,
+ &Credential->LmOwfPassword,
+ sizeof(LanmanSessionKey) );
+ }
+
+ }
+
+
+ //
+ // Allocate a buffer to return to the caller.
+ //
+
+ *ReturnBufferSize = sizeof(MSV1_0_GETCHALLENRESP_RESPONSE) +
+ LogonDomainName.Length + sizeof(WCHAR) +
+ UserName.Length + sizeof(WCHAR) +
+ NtResponseString.Length + sizeof(WCHAR) +
+ LmResponseString.Length + sizeof(WCHAR);
+
+ Status = NlpAllocateClientBuffer( &ClientBufferDesc,
+ sizeof(MSV1_0_GETCHALLENRESP_RESPONSE),
+ *ReturnBufferSize );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ GetRespResponse = (PMSV1_0_GETCHALLENRESP_RESPONSE) ClientBufferDesc.MsvBuffer;
+
+
+ //
+ // Fill in the return buffer.
+ //
+
+ GetRespResponse->MessageType = MsV1_0Lm20GetChallengeResponse;
+ RtlCopyMemory( GetRespResponse->UserSessionKey,
+ &UserSessionKey,
+ sizeof(UserSessionKey));
+ RtlCopyMemory( GetRespResponse->LanmanSessionKey,
+ LanmanSessionKey,
+ sizeof(LanmanSessionKey) );
+
+
+ //
+ // Copy the logon domain name (the string may be empty)
+ //
+
+ NlpPutClientString( &ClientBufferDesc,
+ &GetRespResponse->LogonDomainName,
+ &LogonDomainName );
+
+ //
+ // Copy the user name (the string may be empty)
+ //
+
+ NlpPutClientString( &ClientBufferDesc,
+ &GetRespResponse->UserName,
+ &UserName );
+
+ //
+ // Copy the Challenge Responses to the client buffer.
+ //
+
+ NlpPutClientString(
+ &ClientBufferDesc,
+ (PUNICODE_STRING)
+ &GetRespResponse->CaseSensitiveChallengeResponse,
+ (PUNICODE_STRING) &NtResponseString );
+
+ NlpPutClientString(
+ &ClientBufferDesc,
+ (PUNICODE_STRING)
+ &GetRespResponse->CaseInsensitiveChallengeResponse,
+ (PUNICODE_STRING)&LmResponseString );
+
+
+ //
+ // Flush the buffer to the client's address space.
+ //
+
+ Status = NlpFlushClientBuffer( &ClientBufferDesc,
+ ProtocolReturnBuffer );
+
+Cleanup:
+
+ //
+ // If we weren't successful, free the buffer in the clients address space.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlpFreeClientBuffer( &ClientBufferDesc );
+ }
+
+ //
+ // Cleanup locally used resources
+ //
+
+ if ( BuiltCredential != NULL ) {
+ (*Lsa.FreeLsaHeap)( BuiltCredential );
+ }
+
+ if ( PrimaryCredential != NULL ) {
+ (*Lsa.FreeLsaHeap)( PrimaryCredential );
+ }
+
+ //
+ // Return status to the caller.
+ //
+
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+MspLm20EnumUsers (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of MsV1_0Lm20EnumerateUsers. This routine
+ enumerates all of the interactive, service, and batch logons to the MSV1_0
+ authentication package.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PMSV1_0_ENUMUSERS_REQUEST EnumRequest;
+ PMSV1_0_ENUMUSERS_RESPONSE EnumResponse;
+ CLIENT_BUFFER_DESC ClientBufferDesc;
+ ULONG LogonCount = 0;
+ PACTIVE_LOGON Logon;
+ BOOLEAN ActiveLogonsAreLocked = FALSE;
+
+ PUCHAR Where;
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size and
+ // relocate all of the pointers to be relative to the LSA allocated
+ // buffer.
+ //
+
+ NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
+ UNREFERENCED_PARAMETER( ClientBufferBase );
+
+ if ( SubmitBufferSize < sizeof(MSV1_0_ENUMUSERS_REQUEST) ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ EnumRequest = (PMSV1_0_ENUMUSERS_REQUEST) ProtocolSubmitBuffer;
+
+ ASSERT( EnumRequest->MessageType == MsV1_0EnumerateUsers );
+
+ //
+ // Count the current number of active logons
+ //
+
+ NlpLockActiveLogons();
+ ActiveLogonsAreLocked = TRUE;
+
+ for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {
+ LogonCount ++;
+ }
+
+ //
+ // Allocate a buffer to return to the caller.
+ //
+
+ *ReturnBufferSize = sizeof(MSV1_0_ENUMUSERS_RESPONSE) +
+ LogonCount * (sizeof(LUID) + sizeof(ULONG));
+
+
+ Status = NlpAllocateClientBuffer( &ClientBufferDesc,
+ sizeof(MSV1_0_ENUMUSERS_RESPONSE),
+ *ReturnBufferSize );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ EnumResponse = (PMSV1_0_ENUMUSERS_RESPONSE) ClientBufferDesc.MsvBuffer;
+
+ //
+ // Fill in the return buffer.
+ //
+
+ EnumResponse->MessageType = MsV1_0EnumerateUsers;
+ EnumResponse->NumberOfLoggedOnUsers = LogonCount;
+
+ Where = (PUCHAR)(EnumResponse + 1);
+
+ //
+ // Loop through the Active Logon Table copying the LogonId of each session.
+ //
+
+ EnumResponse->LogonIds = (PLUID)(ClientBufferDesc.UserBuffer +
+ (Where - ClientBufferDesc.MsvBuffer));
+ for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {
+ *((PLUID)Where) = Logon->LogonId,
+ Where += sizeof(LUID);
+ }
+
+ //
+ // Loop through the Active Logon Table copying the EnumHandle of
+ // each session.
+ //
+
+ EnumResponse->EnumHandles = (PULONG)(ClientBufferDesc.UserBuffer +
+ (Where - ClientBufferDesc.MsvBuffer));
+ for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {
+ *((PULONG)Where) = Logon->EnumHandle,
+ Where += sizeof(ULONG);
+ }
+
+ //
+ // Flush the buffer to the client's address space.
+ //
+
+ Status = NlpFlushClientBuffer( &ClientBufferDesc,
+ ProtocolReturnBuffer );
+
+Cleanup:
+
+ //
+ // Be sure to unlock the lock on the Active logon list.
+ //
+
+ if ( ActiveLogonsAreLocked ) {
+ NlpUnlockActiveLogons();
+ }
+
+ //
+ // If we weren't successful, free the buffer in the clients address space.
+ //
+
+ if ( !NT_SUCCESS(Status)) {
+ NlpFreeClientBuffer( &ClientBufferDesc );
+ }
+
+ //
+ // Return status to the caller.
+ //
+
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+MspLm20GetUserInfo (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of MsV1_0GetUserInfo. This routine
+ returns information describing a particular Logon Id.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PMSV1_0_GETUSERINFO_REQUEST GetInfoRequest;
+ PMSV1_0_GETUSERINFO_RESPONSE GetInfoResponse = NULL;
+
+ CLIENT_BUFFER_DESC ClientBufferDesc;
+
+ BOOLEAN ActiveLogonsAreLocked = FALSE;
+ PACTIVE_LOGON *ActiveLogon;
+ PACTIVE_LOGON Logon;
+ ULONG SidLength;
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size and
+ // relocate all of the pointers to be relative to the LSA allocated
+ // buffer.
+ //
+
+ NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
+
+ UNREFERENCED_PARAMETER( ClientBufferBase );
+
+ if ( SubmitBufferSize < sizeof(MSV1_0_GETUSERINFO_REQUEST) ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ GetInfoRequest = (PMSV1_0_GETUSERINFO_REQUEST) ProtocolSubmitBuffer;
+
+ ASSERT( GetInfoRequest->MessageType == MsV1_0GetUserInfo );
+
+ //
+ // Find the Active logon entry for this particular Logon Id.
+ //
+
+ NlpLockActiveLogons();
+ ActiveLogonsAreLocked = TRUE;
+
+ if (!NlpFindActiveLogon( &GetInfoRequest->LogonId, &ActiveLogon )){
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+ goto Cleanup;
+ }
+
+ Logon = *ActiveLogon;
+
+ //
+ // Allocate a buffer to return to the caller.
+ //
+
+ SidLength = RtlLengthSid( Logon->UserSid );
+ *ReturnBufferSize = sizeof(MSV1_0_GETUSERINFO_RESPONSE) +
+ Logon->UserName.Length + sizeof(WCHAR) +
+ Logon->LogonDomainName.Length + sizeof(WCHAR) +
+ Logon->LogonServer.Length + sizeof(WCHAR) +
+ SidLength;
+
+
+ Status = NlpAllocateClientBuffer( &ClientBufferDesc,
+ sizeof(MSV1_0_GETUSERINFO_RESPONSE),
+ *ReturnBufferSize );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ GetInfoResponse = (PMSV1_0_GETUSERINFO_RESPONSE) ClientBufferDesc.MsvBuffer;
+
+
+ //
+ // Fill in the return buffer.
+ //
+
+ GetInfoResponse->MessageType = MsV1_0GetUserInfo;
+ GetInfoResponse->LogonType = Logon->LogonType;
+
+ //
+ // Copy ULONG aligned data first
+ //
+
+ GetInfoResponse->UserSid = ClientBufferDesc.UserBuffer +
+ ClientBufferDesc.StringOffset;
+
+ RtlCopyMemory( ClientBufferDesc.MsvBuffer + ClientBufferDesc.StringOffset,
+ Logon->UserSid,
+ SidLength );
+
+ ClientBufferDesc.StringOffset += SidLength;
+
+ //
+ // Copy WCHAR aligned data
+ //
+
+ NlpPutClientString( &ClientBufferDesc,
+ &GetInfoResponse->UserName,
+ &Logon->UserName );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &GetInfoResponse->LogonDomainName,
+ &Logon->LogonDomainName );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &GetInfoResponse->LogonServer,
+ &Logon->LogonServer );
+
+
+ //
+ // Flush the buffer to the client's address space.
+ //
+
+ Status = NlpFlushClientBuffer( &ClientBufferDesc,
+ ProtocolReturnBuffer );
+
+Cleanup:
+
+ //
+ // Be sure to unlock the lock on the Active logon list.
+ //
+
+ if ( ActiveLogonsAreLocked ) {
+ NlpUnlockActiveLogons();
+ }
+
+ //
+ // If we weren't successful, free the buffer in the clients address space.
+ //
+
+ if ( !NT_SUCCESS(Status)) {
+ NlpFreeClientBuffer( &ClientBufferDesc );
+ }
+
+ //
+ // Return status to the caller.
+ //
+
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+MspLm20ReLogonUsers (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of MsV1_0RelogonUsers. For each logon session
+ which was validated by the specified domain controller, the logon session
+ is re-established with that same domain controller.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+#ifdef LOGON_ENUM_SUPPORTED
+ NTSTATUS Status;
+ PMSV1_0_RELOGON_REQUEST RelogonRequest;
+ PACTIVE_LOGON Logon;
+ BOOLEAN ActiveLogonsAreLocked = FALSE;
+ PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size and
+ // relocate all of the pointers to be relative to the LSA allocated
+ // buffer.
+ //
+
+ UNREFERENCED_PARAMETER( ClientRequest );
+ UNREFERENCED_PARAMETER( ReturnBufferSize );
+
+ if ( SubmitBufferSize < sizeof(MSV1_0_RELOGON_REQUEST) ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ RelogonRequest = (PMSV1_0_RELOGON_REQUEST) ProtocolSubmitBuffer;
+
+ ASSERT( RelogonRequest->MessageType == MsV1_0ReLogonUsers );
+
+ RELOCATE_ONE( &RelogonRequest->LogonServer );
+
+ //
+ // Count the current number of active logons
+ //
+
+ NlpLockActiveLogons();
+ ActiveLogonsAreLocked = TRUE;
+
+ //
+ // Loop through the Active Logon Table
+ // relogging on each entry for the specified server.
+ //
+
+ for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {
+ NETLOGON_INTERACTIVE_INFO LogonInteractive;
+ PNETLOGON_VALIDATION_SAM_INFO2 NlpUser;
+ BOOLEAN Authoritative;
+
+ //
+ // Ensure this entry was originally logged on by the Netlogon Service
+ // on the specified server.
+ //
+
+ if ( (Logon->Flags & LOGON_BY_NETLOGON) == 0 ||
+ !RtlEqualComputerName( &RelogonRequest->LogonServer,
+ &Logon->LogonServer ) ) {
+
+ continue;
+ }
+
+ //
+ // This shouldn't happen since LOGON_BY_NETLOGON is set.
+ //
+
+ if ( NlpNetlogonDllHandle == NULL ) {
+ continue;
+ }
+
+ //
+ // Get the OWF password for this session.
+ //
+
+ Status = NlpGetPrimaryCredential( &Logon->LogonId, &Credential, NULL );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Define the description of the user to log on.
+ //
+
+ LogonInteractive.Identity.LogonDomainName = Logon->LogonDomainName;
+ LogonInteractive.Identity.ParameterControl = 0;
+ LogonInteractive.Identity.LogonId = Logon->LogonId;
+ LogonInteractive.Identity.UserName = Logon->UserName;
+ LogonInteractive.Identity.Workstation = NlpComputerName;
+ LogonInteractive.LmOwfPassword = Credential->LmOwfPassword;
+ LogonInteractive.NtOwfPassword = Credential->NtOwfPassword;
+
+ //
+ // Wait for NETLOGON to finish initialization.
+ //
+
+ if ( !NlpNetlogonInitialized ) {
+
+ Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 45 );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ NlpNetlogonInitialized = TRUE;
+ }
+
+
+ //
+ // Call the Netlogon Service.
+ //
+ // The credentials stored in the Credential structure are really
+ // the OWF encrypted password.
+ //
+
+
+ Status = (*NlpNetLogonSamLogon)(
+ NULL, // Server name
+ NULL, // Computer name
+ NULL, // Authenticator
+ NULL, // ReturnAuthenticator
+ NetlogonInteractiveInformation,
+ (LPBYTE) &LogonInteractive,
+ NetlogonValidationSamInfo2,
+ (LPBYTE *) &NlpUser,
+ &Authoritative );
+
+ //
+ // Ignore failures.
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ MIDL_user_free( NlpUser );
+ }
+
+ }
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ //
+ // Be sure to unlock the lock on the Active logon list.
+ //
+
+ if ( ActiveLogonsAreLocked ) {
+ NlpUnlockActiveLogons();
+ }
+
+ if ( Credential != NULL ) {
+ (*Lsa.FreeLsaHeap)( Credential );
+ }
+
+
+ //
+ // Return status to the caller.
+ //
+
+ *ProtocolReturnBuffer = NULL;
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+#else // LOGON_ENUM_SUPPORTED
+ UNREFERENCED_PARAMETER( ClientRequest );
+ UNREFERENCED_PARAMETER( ProtocolSubmitBuffer);
+ UNREFERENCED_PARAMETER( ClientBufferBase);
+ UNREFERENCED_PARAMETER( SubmitBufferSize);
+ UNREFERENCED_PARAMETER( ReturnBufferSize);
+
+ *ProtocolReturnBuffer = NULL;
+ *ProtocolStatus = STATUS_NOT_IMPLEMENTED;
+ return STATUS_SUCCESS;
+#endif // LOGON_ENUM_SUPPORTED
+
+}
+
+
+
+
+NTSTATUS
+LsaApLogonUserEx (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ OUT PLUID LogonId,
+ OUT PNTSTATUS SubStatus,
+ OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ OUT PVOID *TokenInformation,
+ OUT PUNICODE_STRING *AccountName,
+ OUT PUNICODE_STRING *AuthenticatingAuthority,
+ OUT PUNICODE_STRING *MachineName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to authenticate a user logon attempt. This is
+ the user's initial logon. A new LSA logon session will be established
+ for the user and validation information for the user will be returned.
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ LogonType - Identifies the type of logon being attempted.
+
+ ProtocolSubmitBuffer - Supplies the authentication
+ information specific to the authentication package.
+
+ ClientBufferBase - Provides the address within the client
+ process at which the authentication information was resident.
+ This may be necessary to fix-up any pointers within the
+ authentication information buffer.
+
+ SubmitBufferSize - Indicates the Size, in bytes,
+ of the authentication information buffer.
+
+ ProfileBuffer - Is used to return the address of the profile
+ buffer in the client process. The authentication package is
+ responsible for allocating and returning the profile buffer
+ within the client process. However, if the LSA subsequently
+ encounters an error which prevents a successful logon, then
+ the LSA will take care of deallocating that buffer. This
+ buffer is expected to have been allocated with the
+ AllocateClientBuffer() service.
+
+ The format and semantics of this buffer are specific to the
+ authentication package.
+
+ ProfileBufferSize - Receives the Size (in bytes) of the
+ returned profile buffer.
+
+ SubStatus - If the logon failed due to account restrictions, the
+ reason for the failure should be returned via this parameter.
+ The reason is authentication-package specific. The substatus
+ values for authentication package "MSV1.0" are:
+
+ STATUS_INVALID_LOGON_HOURS
+
+ STATUS_INVALID_WORKSTATION
+
+ STATUS_PASSWORD_EXPIRED
+
+ STATUS_ACCOUNT_DISABLED
+
+ TokenInformationLevel - If the logon is successful, this field is
+ used to indicate what level of information is being returned
+ for inclusion in the Token to be created. This information
+ is returned via the TokenInformation parameter.
+
+ TokenInformation - If the logon is successful, this parameter is
+ used by the authentication package to return information to
+ be included in the token. The format and content of the
+ buffer returned is indicated by the TokenInformationLevel
+ return value.
+
+ AccountName - A Unicode string describing the account name
+ being logged on to. This parameter must always be returned
+ regardless of the success or failure of the operation.
+
+ AuthenticatingAuthority - A Unicode string describing the Authenticating
+ Authority for the logon. This string may optionally be omitted.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+ STATUS_NO_LOGON_SERVERS - Indicates that no domain controllers
+ are currently able to service the authentication request.
+
+ STATUS_LOGON_FAILURE - Indicates the logon attempt failed. No
+ indication as to the reason for failure is given, but typical
+ reasons include mispelled usernames, mispelled passwords.
+
+ STATUS_ACCOUNT_RESTRICTION - Indicates the user account and
+ password were legitimate, but that the user account has some
+ restriction preventing successful logon at this time.
+
+ STATUS_BAD_VALIDATION_CLASS - The authentication information
+ provided is not a validation class known to the specified
+ authentication package.
+
+ STATUS_INVALID_LOGON_CLASS - LogonType was invalid.
+
+ STATUS_LOGON_SESSION_COLLISION - Internal Error: A LogonId was selected for
+ this logon session. The selected LogonId already exists.
+
+ STATUS_NETLOGON_NOT_STARTED - The Sam Server or Netlogon service was
+ required to perform this function. The required server was not running.
+
+ STATUS_NO_MEMORY - Insufficient virtual memory or pagefile quota exists.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSA_TOKEN_INFORMATION_TYPE LsaTokenInformationType = LsaTokenInformationV1;
+
+ PNETLOGON_VALIDATION_SAM_INFO2 NlpUser = NULL;
+
+
+ PACTIVE_LOGON LogonEntry = NULL;
+ BOOLEAN LogonEntryLinked = FALSE;
+
+ BOOLEAN LogonSessionCreated = FALSE;
+ BOOLEAN LogonCredentialAdded = FALSE;
+ ULONG Flags = 0;
+ BOOLEAN Authoritative;
+ BOOLEAN BadPasswordCountZeroed;
+ BOOLEAN StandaloneWorkstation;
+
+ PSID UserSid = NULL;
+
+ PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
+ ULONG CredentialSize;
+
+ PSECURITY_SEED_AND_LENGTH SeedAndLength;
+ UCHAR Seed;
+
+ PUNICODE_STRING WorkStationName = NULL;
+
+ //
+ // Temporary storage while we try to figure
+ // out what our username and authenticating
+ // authority is.
+ //
+
+ UNICODE_STRING TmpName;
+ WCHAR TmpNameBuffer[UNLEN];
+ UNICODE_STRING TmpAuthority;
+ WCHAR TmpAuthorityBuffer[DNLEN];
+
+ //
+ // Logon Information.
+ //
+ NETLOGON_LOGON_INFO_CLASS LogonLevel;
+ NETLOGON_INTERACTIVE_INFO LogonInteractive;
+ NETLOGON_NETWORK_INFO LogonNetwork;
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInformation;
+
+ //
+ // Initialize
+ //
+
+ *ProfileBuffer = NULL;
+ *SubStatus = STATUS_SUCCESS;
+ *AuthenticatingAuthority = NULL;
+ *AccountName = NULL;
+
+ TmpName.Buffer = TmpNameBuffer;
+ TmpName.MaximumLength = UNLEN * sizeof( WCHAR );
+ TmpName.Length = 0;
+
+ TmpAuthority.Buffer = TmpAuthorityBuffer;
+ TmpAuthority.MaximumLength = DNLEN * sizeof( WCHAR );
+ TmpAuthority.Length = 0;
+
+ //
+ // Check the Authentication information and build a LogonInformation
+ // structure to pass to SAM or Netlogon.
+ //
+ // NOTE: Netlogon treats Service and Batch logons as if they are
+ // Interactive.
+ //
+
+ switch ( LogonType ) {
+ case Interactive:
+ case Service:
+ case Batch:
+ {
+ PMSV1_0_INTERACTIVE_LOGON Authentication;
+
+ WorkStationName = &NlpComputerName;
+
+ //
+ // Ensure this is really an interactive logon.
+ //
+
+ Authentication =
+ (PMSV1_0_INTERACTIVE_LOGON) ProtocolSubmitBuffer;
+
+ if ( Authentication->MessageType != MsV1_0InteractiveLogon ) {
+ KdPrint(("MSV1_0: LsaApLogonUser: Bad Validation Class\n"));
+ Status = STATUS_BAD_VALIDATION_CLASS;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // If the password length is greater than 255 (i.e., the
+ // upper byte of the length is non-zero) then the password
+ // has been run-encoded for privacy reasons. Get the
+ // run-encode seed out of the upper-byte of the length
+ // for later use.
+ //
+ //
+
+
+
+ SeedAndLength = (PSECURITY_SEED_AND_LENGTH)
+ &Authentication->Password.Length;
+ Seed = SeedAndLength->Seed;
+ SeedAndLength->Seed = 0;
+
+ //
+ // Enforce length restrictions on username and password.
+ //
+
+ if ( Authentication->UserName.Length > UNLEN ||
+ Authentication->Password.Length > PWLEN ) {
+ KdPrint(("MSV1_0: LsaApLogonUser: Name or password too long\n"));
+ Status = STATUS_NAME_TOO_LONG;
+ goto Cleanup;
+ }
+
+
+ //
+ // Relocate any pointers to be relative to 'Authentication'
+ //
+
+ NULL_RELOCATE_ONE( &Authentication->LogonDomainName );
+
+ RELOCATE_ONE( &Authentication->UserName );
+
+ NULL_RELOCATE_ONE( &Authentication->Password );
+
+
+ //
+ // Now decode the password, if necessary
+ //
+
+ if (Seed != 0 ) {
+ try {
+ RtlRunDecodeUnicodeString( Seed, &Authentication->Password);
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ Status = STATUS_ILL_FORMED_PASSWORD;
+ goto Cleanup;
+ }
+ }
+
+ //
+ // Copy out the user name and Authenticating Authority so we can audit them.
+ //
+
+ RtlCopyUnicodeString( &TmpName, &Authentication->UserName );
+
+ if ( Authentication->LogonDomainName.Buffer != NULL ) {
+
+ RtlCopyUnicodeString( &TmpAuthority, &Authentication->LogonDomainName );
+ }
+
+ //
+ // Build the primary credential
+ //
+
+ Status = NlpMakePrimaryCredential( &Authentication->LogonDomainName,
+ &Authentication->UserName,
+ &Authentication->Password,
+ &Credential,
+ &CredentialSize );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // We're all done with the cleartext password
+ // Don't let it get to the pagefile.
+ //
+
+ try {
+ if ( Authentication->Password.Buffer != NULL ) {
+ RtlEraseUnicodeString( &Authentication->Password );
+ }
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ Status = STATUS_ILL_FORMED_PASSWORD;
+ goto Cleanup;
+ }
+
+
+ //
+ // Define the description of the user to log on.
+ //
+ LogonLevel = NetlogonInteractiveInformation;
+ LogonInformation =
+ (PNETLOGON_LOGON_IDENTITY_INFO) &LogonInteractive;
+
+ LogonInteractive.Identity.LogonDomainName =
+ Authentication->LogonDomainName;
+ LogonInteractive.Identity.ParameterControl = 0;
+
+ LogonInteractive.Identity.UserName = Authentication->UserName;
+ LogonInteractive.Identity.Workstation = NlpComputerName;
+
+
+ LogonInteractive.LmOwfPassword = Credential->LmOwfPassword;
+ LogonInteractive.NtOwfPassword = Credential->NtOwfPassword;
+
+ }
+
+ break;
+
+ case Network:
+ {
+ PMSV1_0_LM20_LOGON Authentication;
+
+ //
+ // Ensure this is really a network logon request.
+ //
+
+ Authentication =
+ (PMSV1_0_LM20_LOGON) ProtocolSubmitBuffer;
+
+ if ( Authentication->MessageType != MsV1_0Lm20Logon &&
+ Authentication->MessageType != MsV1_0NetworkLogon ) {
+ KdPrint(("MSV1_0: LsaApLogonUser: Bad Validation Class\n"));
+ Status = STATUS_BAD_VALIDATION_CLASS;
+ goto Cleanup;
+ }
+
+
+ //
+ // Relocate any pointers to be relative to 'Authentication'
+ //
+
+ NULL_RELOCATE_ONE( &Authentication->LogonDomainName );
+
+ NULL_RELOCATE_ONE( &Authentication->UserName );
+
+ RELOCATE_ONE( &Authentication->Workstation );
+
+ //
+ // Copy out the user name and Authenticating Authority so we can audit them.
+ //
+
+ if ( Authentication->UserName.Buffer != NULL ) {
+
+ RtlCopyUnicodeString( &TmpName, &Authentication->UserName );
+ }
+
+ if ( Authentication->LogonDomainName.Buffer != NULL ) {
+
+ RtlCopyUnicodeString( &TmpAuthority, &Authentication->LogonDomainName );
+ }
+
+ NULL_RELOCATE_ONE((PUNICODE_STRING)&Authentication->CaseSensitiveChallengeResponse );
+
+ NULL_RELOCATE_ONE((PUNICODE_STRING)&Authentication->CaseInsensitiveChallengeResponse );
+
+
+ //
+ // Define the description of the user to log on.
+ //
+ LogonLevel = NetlogonNetworkInformation;
+ LogonInformation =
+ (PNETLOGON_LOGON_IDENTITY_INFO) &LogonNetwork;
+
+ LogonNetwork.Identity.LogonDomainName =
+ Authentication->LogonDomainName;
+
+ if ( Authentication->MessageType == MsV1_0Lm20Logon ) {
+ LogonNetwork.Identity.ParameterControl = CLEARTEXT_PASSWORD_ALLOWED;
+ } else {
+ ASSERT( CLEARTEXT_PASSWORD_ALLOWED == MSV1_0_CLEARTEXT_PASSWORD_ALLOWED );
+ LogonNetwork.Identity.ParameterControl =
+ Authentication->ParameterControl;
+ }
+
+ LogonNetwork.Identity.UserName = Authentication->UserName;
+ LogonNetwork.Identity.Workstation = Authentication->Workstation;
+
+ WorkStationName = &Authentication->Workstation;
+
+ LogonNetwork.NtChallengeResponse =
+ Authentication->CaseSensitiveChallengeResponse;
+ LogonNetwork.LmChallengeResponse =
+ Authentication->CaseInsensitiveChallengeResponse;
+ ASSERT( LM_CHALLENGE_LENGTH ==
+ sizeof(Authentication->ChallengeToClient) );
+ RtlCopyMemory( &LogonNetwork.LmChallenge,
+ Authentication->ChallengeToClient,
+ LM_CHALLENGE_LENGTH );
+
+ //
+ // Enforce length restrictions on username
+ //
+
+ if ( Authentication->UserName.Length > UNLEN ) {
+ KdPrint(("MSV1_0: LsaApLogonUser: Name too long\n"));
+ Status = STATUS_NAME_TOO_LONG;
+ goto Cleanup;
+ }
+
+ //
+ // If this is a null session logon,
+ // just build a NULL token.
+ //
+
+ if ( Authentication->UserName.Length == 0 &&
+ Authentication->CaseSensitiveChallengeResponse.Length == 0 &&
+ (Authentication->CaseInsensitiveChallengeResponse.Length == 0 ||
+ (Authentication->CaseInsensitiveChallengeResponse.Length == 1 &&
+ *Authentication->CaseInsensitiveChallengeResponse.Buffer == '\0') ) ) {
+
+ LsaTokenInformationType = LsaTokenInformationNull;
+ }
+ }
+
+ break;
+
+ default:
+ return STATUS_INVALID_LOGON_TYPE;
+
+ }
+
+
+ //
+ // Allocate a LogonId for this logon session.
+ //
+
+ Status = NtAllocateLocallyUniqueId( LogonId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ NEW_TO_OLD_LARGE_INTEGER( (*LogonId), LogonInformation->LogonId );
+
+
+ //
+ // Create a new logon session
+ //
+
+ Status = (*Lsa.CreateLogonSession)( LogonId );
+ if( !NT_SUCCESS(Status) ) {
+ KdPrint(( "MSV1_0: LsaApLogonUser: Collision from CreateLogonSession\n"));
+ goto Cleanup;
+ }
+
+ LogonSessionCreated = TRUE;
+
+
+ //
+ // Don't worry about SAM or the LSA if this is a Null Session logon.
+ //
+ // The server does a Null Session logon during initialization.
+ // It shouldn't have to wait for SAM to initialize.
+ //
+
+ if ( LsaTokenInformationType != LsaTokenInformationNull ) {
+
+ //
+ // Try to load NetlogonDll again if it isn't already.
+ //
+
+ if ( NlpNetlogonDllHandle == NULL ) {
+ NlpLoadNetlogonDll();
+ }
+
+ //
+ // If Sam is not yet initialized,
+ // do it now.
+ //
+
+ if ( !NlpSamInitialized ) {
+ Status = NlSamInitialize( 120 );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ }
+
+ //
+ // If this is a workstation,
+ // differentiate between a standalone workstation and a member
+ // workstation.
+ //
+ // (This is is done on every logon, rather than during initialization,
+ // to allow the value to be changed via the UI).
+ //
+
+ if ( NlpWorkstation && NlpPolicyHandle != NULL ) {
+ PLSAPR_POLICY_INFORMATION PolicyPrimaryDomainInfo = NULL;
+
+ Status = LsarQueryInformationPolicy(
+ NlpPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ &PolicyPrimaryDomainInfo );
+
+ if ( NT_SUCCESS(Status) ) {
+ StandaloneWorkstation =
+ (PolicyPrimaryDomainInfo->PolicyPrimaryDomainInfo.Sid == NULL);
+
+ LsaIFree_LSAPR_POLICY_INFORMATION( PolicyPrimaryDomainInformation,
+ PolicyPrimaryDomainInfo );
+
+ } else {
+ StandaloneWorkstation = FALSE;
+ }
+
+ } else {
+ StandaloneWorkstation = FALSE;
+ }
+ }
+
+
+ //
+ // Do the actual logon now.
+ //
+ //
+ // If a null token is being built,
+ // don't authenticate at all.
+ //
+
+ if ( LsaTokenInformationType == LsaTokenInformationNull ) {
+
+ /* Nothing to do here. */
+
+
+ //
+ // Call Sam directly to get the validation information when:
+ //
+ // The network is not installed, OR
+ // This is a standalone workstation (not a member of a domain).
+ // This is a workstation and we're logging onto an account on the
+ // workstation.
+ //
+
+ } else if ( NlpNetlogonDllHandle == NULL || !NlpLanmanInstalled ||
+ StandaloneWorkstation ||
+ ( NlpWorkstation &&
+ LogonInformation->LogonDomainName.Length != 0 &&
+ RtlEqualDomainName( &NlpSamDomainName,
+ &LogonInformation->LogonDomainName )) ) {
+
+ //
+ // Get the Validation information from the local SAM database
+ //
+
+ Status = MsvSamValidate(
+ NlpSamDomainHandle,
+ NlpUasCompatibilityRequired,
+ MsvApSecureChannel,
+ &NlpComputerName, // Logon Server is this machine
+ &NlpSamDomainName,
+ NlpSamDomainId,
+ LogonLevel,
+ LogonInformation,
+ NetlogonValidationSamInfo2,
+ (PVOID *) &NlpUser,
+ &Authoritative,
+ &BadPasswordCountZeroed,
+ MSVSAM_SPECIFIED | MSVSAM_GUEST);
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // If we couldn't validate via one of the above mechanisms,
+ // call the local Netlogon service to get the validation information.
+ //
+
+ } else {
+
+ //
+ // Wait for NETLOGON to finish initialization.
+ //
+
+ if ( !NlpNetlogonInitialized ) {
+
+ Status = NlWaitForNetlogon( 90 );
+
+ if ( !NT_SUCCESS(Status) ) {
+ if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
+ goto Cleanup;
+ }
+ } else {
+ NlpNetlogonInitialized = TRUE;
+ }
+ }
+
+ //
+ // Actually call the netlogon service.
+ //
+
+ if ( NlpNetlogonInitialized ) {
+ Status = (*NlpNetLogonSamLogon)(
+ NULL, // Server name
+ NULL, // Computer name
+ NULL, // Authenticator
+ NULL, // ReturnAuthenticator
+ LogonLevel,
+ (LPBYTE) &LogonInformation,
+ NetlogonValidationSamInfo2,
+ (LPBYTE *) &NlpUser,
+ &Authoritative );
+
+ //
+ // Reset Netlogon initialized flag if local netlogon cannot be
+ // reached.
+ // (Use a more explicit status code)
+ //
+
+ if ( Status == RPC_NT_SERVER_UNAVAILABLE ||
+ Status == RPC_NT_UNKNOWN_IF ||
+ Status == STATUS_NETLOGON_NOT_STARTED ) {
+ Status = STATUS_NETLOGON_NOT_STARTED;
+ NlpNetlogonInitialized = FALSE;
+ }
+ }
+
+
+ //
+ // If this is the requested domain,
+ // go directly to SAM if the netlogon service isn't available.
+ //
+ // We want to go to the netlogon service if it is available since it
+ // does special handling of bad passwords and account lockout. However,
+ // if the netlogon service is down, the local SAM database makes a
+ // better cache than any other mechanism.
+ //
+
+ if ( !NlpNetlogonInitialized &&
+ LogonInformation->LogonDomainName.Length != 0 &&
+ RtlEqualDomainName( &NlpSamDomainName,
+ &LogonInformation->LogonDomainName ) ) {
+
+ //
+ // Get the Validation information from the local SAM database
+ //
+
+ Status = MsvSamValidate(
+ NlpSamDomainHandle,
+ NlpUasCompatibilityRequired,
+ MsvApSecureChannel,
+ &NlpComputerName, // Logon Server is this machine
+ &NlpSamDomainName,
+ NlpSamDomainId,
+ LogonLevel,
+ LogonInformation,
+ NetlogonValidationSamInfo2,
+ (PVOID *) &NlpUser,
+ &Authoritative,
+ &BadPasswordCountZeroed,
+ MSVSAM_SPECIFIED | MSVSAM_GUEST);
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // If Netlogon was successful,
+ // add this user to the logon cache.
+ //
+
+ } else if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Indicate this session was validated by the Netlogon
+ // service.
+ //
+
+ Flags |= LOGON_BY_NETLOGON;
+
+ //
+ // Cache interactive logon information.
+ //
+ // NOTE: Batch and Service logons are not treated
+ // the same as Interactive here.
+ //
+
+ if (LogonType == Interactive) {
+
+ NTSTATUS ntStatus;
+
+ ntStatus = NlpAddCacheEntry(&LogonInteractive, NlpUser);
+ }
+
+ //
+ // If Netlogon is simply not available at this time,
+ // try to logon through the cache.
+ //
+ // STATUS_NO_LOGON_SERVERS indicates the netlogon service couldn't
+ // contact a DC to handle this request.
+ //
+ // STATUS_NETLOGON_NOT_STARTED indicates the local netlogon service
+ // isn't running.
+ //
+ //
+ // Even though we change the cache only for interactive logons,
+ // we use the cache for ANY logon type. This not only allows a
+ // user to logon interactively, but it allows that same user to
+ // connect from another machine while the DC is down.
+ //
+
+ } else if ( Status == STATUS_NO_LOGON_SERVERS ||
+ Status == STATUS_NETLOGON_NOT_STARTED ) {
+
+ NTSTATUS ntStatus;
+ CACHE_PASSWORDS cachePasswords;
+
+ //
+ // Try to logon via the cache.
+ //
+ //
+
+ ntStatus = NlpGetCacheEntry(LogonInformation, &NlpUser, &cachePasswords);
+
+ if (!NT_SUCCESS(ntStatus)) {
+
+ //
+ // The original status code is more interesting than
+ // the fact that the cache didn't work.
+ //
+
+ NlpUser = NULL; // NlpGetCacheEntry dirties this
+ goto Cleanup;
+ }
+
+ //
+ // Now we have the information from the cache, validate the
+ // user's password
+ //
+
+ if (!MsvpPasswordValidate(
+ NlpUasCompatibilityRequired,
+ LogonLevel,
+ (PVOID)LogonInformation,
+ &cachePasswords.SecretPasswords,
+ &NlpUser->UserFlags,
+ &NlpUser->UserSessionKey,
+ (PLM_SESSION_KEY)
+ &NlpUser->ExpansionRoom[SAMINFO_LM_SESSION_KEY]
+ )) {
+ Status = STATUS_WRONG_PASSWORD;
+ goto Cleanup;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // The cache always returns a NETLOGONV_VALIDATION_SAM_INFO2
+ // structure so set the LOGON_EXTRA_SIDS flag, whether or not
+ // there are extra sids
+ //
+
+ NlpUser->UserFlags |= LOGON_CACHED_ACCOUNT | LOGON_EXTRA_SIDS;
+ Flags |= LOGON_BY_CACHE;
+
+ //
+ // If the account is permanently dead on the domain controller,
+ // Flush this entry from the cache.
+ //
+ // Notice that STATUS_INVALID_LOGON_HOURS is not in the list below.
+ // This ensures a user will be able to remove his portable machine
+ // from the net and use it after hours.
+ //
+ // Notice the STATUS_WRONG_PASSWORD is not in the list below.
+ // We're as likely to flush the cache for typo'd passwords as anything
+ // else. What we'd really like to do is flush the cache if the
+ // password on the DC is different than the one in cache; but that's
+ // impossible to detect.
+ //
+ // ONLY DO THIS FOR INTERACTIVE LOGONS
+ // (not Service or Batch).
+ //
+
+ } else if ( LogonType == Interactive &&
+ (Status == STATUS_NO_SUCH_USER ||
+ Status == STATUS_INVALID_WORKSTATION ||
+ Status == STATUS_PASSWORD_EXPIRED ||
+ Status == STATUS_ACCOUNT_DISABLED) ) {
+
+ //
+ // Delete the cache entry
+
+ NTSTATUS ntStatus;
+
+ ntStatus = NlpDeleteCacheEntry(&LogonInteractive);
+ KdPrint(("MSV1_0: LsaApLogonUser: NlpDeleteCacheEntry returns %x\n", ntStatus));
+
+ goto Cleanup;
+
+ } else {
+
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // For everything except network logons,
+ // save the credentials in the LSA,
+ // create active logon table entry,
+ // return the interactive profile buffer.
+ //
+
+ if ( LogonType == Interactive ||
+ LogonType == Service ||
+ LogonType == Batch
+ ) {
+ PACTIVE_LOGON *ActiveLogon;
+ ULONG LogonEntrySize;
+ ULONG UserSidSize;
+ PUCHAR Where;
+ USHORT LogonCount;
+
+
+ //
+ // Save the credential in the LSA.
+ //
+
+ Status = NlpAddPrimaryCredential( LogonId,
+ Credential,
+ CredentialSize );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint(( "MSV1_0: LsaApLogonUser: error from AddCredential %lX\n",
+ Status));
+ goto Cleanup;
+ }
+ LogonCredentialAdded = TRUE;
+
+
+ //
+ // Build a Sid for this user.
+ //
+
+ UserSid = NlpMakeDomainRelativeSid( NlpUser->LogonDomainId,
+ NlpUser->UserId );
+
+ if ( UserSid == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ KdPrint(("MSV1_0: LsaApLogonUser: No memory\n"));
+ goto Cleanup;
+ }
+
+ UserSidSize = RtlLengthSid( UserSid );
+
+
+ //
+ // Allocate an entry for the active logon table.
+ //
+
+ LogonEntrySize = ROUND_UP_COUNT(sizeof(ACTIVE_LOGON), ALIGN_DWORD) +
+ ROUND_UP_COUNT(UserSidSize, sizeof(WCHAR)) +
+ NlpUser->EffectiveName.Length + sizeof(WCHAR) +
+ NlpUser->LogonDomainName.Length + sizeof(WCHAR) +
+ NlpUser->LogonServer.Length + sizeof(WCHAR);
+
+ LogonEntry = RtlAllocateHeap( MspHeap, 0, LogonEntrySize );
+
+ if ( LogonEntry == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ KdPrint(("MSV1_0: LsaApLogonUser: No memory %ld\n",
+ sizeof(ACTIVE_LOGON)));
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the logon table entry.
+ //
+
+ Where = (PUCHAR)(LogonEntry + 1);
+
+ OLD_TO_NEW_LARGE_INTEGER(
+ LogonInformation->LogonId,
+ LogonEntry->LogonId );
+
+ LogonEntry->Flags = Flags;
+ LogonEntry->LogonType = LogonType;
+
+ //
+ // Copy DWORD aligned fields first.
+ //
+
+ Where = ROUND_UP_POINTER( Where, ALIGN_DWORD );
+ Status = RtlCopySid(UserSidSize, (PSID)Where, UserSid);
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ LogonEntry->UserSid = (PSID) Where;
+ Where += UserSidSize;
+
+ //
+ // Copy WCHAR aligned fields
+ //
+
+ Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
+ NlpPutString( &LogonEntry->UserName,
+ &NlpUser->EffectiveName,
+ &Where );
+
+ NlpPutString( &LogonEntry->LogonDomainName,
+ &NlpUser->LogonDomainName,
+ &Where );
+
+ NlpPutString( &LogonEntry->LogonServer,
+ &NlpUser->LogonServer,
+ &Where );
+
+
+ //
+ // Get the next enumeration handle for this session.
+ //
+
+ NlpLockActiveLogons();
+
+ NlpEnumerationHandle ++;
+ LogonEntry->EnumHandle = NlpEnumerationHandle;
+
+ //
+ // Insert this entry into the active logon table.
+ //
+
+ if (NlpFindActiveLogon( LogonId, &ActiveLogon )){
+
+ //
+ // This Logon ID is already in use.
+ //
+
+ NlpUnlockActiveLogons();
+
+ Status = STATUS_LOGON_SESSION_COLLISION;
+ KdPrint((
+ "MSV1_0: LsaApLogonUser: Collision from NlpFindActiveLogon\n"));
+ goto Cleanup;
+ }
+
+ LogonEntry->Next = *ActiveLogon;
+ *ActiveLogon = LogonEntry;
+ LogonEntryLinked = TRUE;
+ NlpUnlockActiveLogons();
+
+
+ //
+ // Ensure the LogonCount is at least as big as it is for this
+ // machine.
+ //
+
+ LogonCount = (USHORT) NlpCountActiveLogon( &NlpUser->LogonDomainName,
+ &NlpUser->EffectiveName );
+ if ( NlpUser->LogonCount < LogonCount ) {
+ NlpUser->LogonCount = LogonCount;
+ }
+
+ //
+ // Alocate the profile buffer to return to the client
+ //
+
+ Status = NlpAllocateInteractiveProfile(
+ ClientRequest,
+ (PMSV1_0_INTERACTIVE_PROFILE *) ProfileBuffer,
+ ProfileBufferSize,
+ NlpUser );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint((
+ "MSV1_0: LsaApLogonUser: Allocate Profile Failed: %lx\n", Status));
+ goto Cleanup;
+ }
+
+ } else if ( LogonType == Network ) {
+
+ //
+ // Alocate the profile buffer to return to the client
+ //
+
+ Status = NlpAllocateNetworkProfile(
+ ClientRequest,
+ (PMSV1_0_LM20_LOGON_PROFILE *) ProfileBuffer,
+ ProfileBufferSize,
+ NlpUser,
+ LogonNetwork.Identity.ParameterControl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint((
+ "MSV1_0: LsaApLogonUser: Allocate Profile Failed: %lx\n", Status));
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // Build the token information to return to the LSA
+ //
+
+ switch (LsaTokenInformationType) {
+ case LsaTokenInformationV1:
+
+ Status = NlpMakeTokenInformationV1(
+ NlpUser,
+ (PLSA_TOKEN_INFORMATION_V1 *)TokenInformation );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint((
+ "MSV1_0: LsaApLogonUser: MakeTokenInformation Failed: %lx\n", Status));
+ goto Cleanup;
+ }
+ break;
+
+ case LsaTokenInformationNull:
+ {
+ PLSA_TOKEN_INFORMATION_NULL VNull;
+
+ VNull = (*Lsa.AllocateLsaHeap)(sizeof(LSA_TOKEN_INFORMATION_NULL) );
+ if ( VNull == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ VNull->Groups = NULL;
+
+ VNull->ExpirationTime.HighPart = 0x7FFFFFFF;
+ VNull->ExpirationTime.LowPart = 0xFFFFFFFF;
+
+ *TokenInformation = VNull;
+ }
+
+ }
+
+ *TokenInformationType = LsaTokenInformationType;
+
+ //
+ // Copy out the AuthenticatingAuthority here. This is for auditing.
+ //
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ //
+ // If the logon wasn't successful,
+ // cleanup resources we would have returned to the caller.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if ( LogonSessionCreated ) {
+ (VOID)(*Lsa.DeleteLogonSession)( LogonId );
+ }
+
+ if ( LogonEntry != NULL ) {
+ if ( LogonEntryLinked ) {
+ LsaApLogonTerminated( LogonId );
+ } else {
+ if ( LogonCredentialAdded ) {
+ (VOID) NlpDeletePrimaryCredential(
+ LogonId );
+ }
+ RtlFreeHeap( MspHeap, 0, LogonEntry );
+ }
+ }
+
+ if ( *ProfileBuffer != NULL ) {
+ (VOID)(*Lsa.FreeClientBuffer)( ClientRequest, *ProfileBuffer );
+ *ProfileBuffer = NULL;
+ }
+
+ }
+
+ //
+ // Copy out Authenticating authority and user name.
+ //
+
+ if ( NT_SUCCESS(Status) && LsaTokenInformationType != LsaTokenInformationNull ) {
+
+ //
+ // Use the information from the NlpUser structure, since it gives
+ // us accurate information about what account we're logging on to,
+ // rather than who we were.
+ //
+
+ if ( LogonType != Network ) {
+ TmpName = NlpUser->EffectiveName;
+ }
+
+ TmpAuthority = NlpUser->LogonDomainName;
+ }
+
+ *AccountName = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
+
+ if ( *AccountName != NULL ) {
+
+ (*AccountName)->Buffer = (*Lsa.AllocateLsaHeap)(TmpName.Length + sizeof( UNICODE_NULL) );
+
+ if ( (*AccountName)->Buffer != NULL ) {
+
+ (*AccountName)->MaximumLength = TmpName.Length + sizeof( UNICODE_NULL );
+ RtlCopyUnicodeString( *AccountName, &TmpName );
+
+ } else {
+
+ RtlInitUnicodeString( *AccountName, NULL );
+ }
+ }
+
+ *AuthenticatingAuthority = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
+
+ if ( *AuthenticatingAuthority != NULL ) {
+
+ (*AuthenticatingAuthority)->Buffer = (*Lsa.AllocateLsaHeap)( TmpAuthority.Length + sizeof( UNICODE_NULL ) );
+
+ if ( (*AuthenticatingAuthority)->Buffer != NULL ) {
+
+ (*AuthenticatingAuthority)->MaximumLength = (USHORT)(TmpAuthority.Length + sizeof( UNICODE_NULL ));
+ RtlCopyUnicodeString( *AuthenticatingAuthority, &TmpAuthority );
+
+ } else {
+
+ RtlInitUnicodeString( *AuthenticatingAuthority, NULL );
+ }
+ }
+
+ *MachineName = NULL;
+
+ if (WorkStationName != NULL) {
+
+ *MachineName = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
+
+ if ( *MachineName != NULL ) {
+
+ (*MachineName)->Buffer = (*Lsa.AllocateLsaHeap)( WorkStationName->Length + sizeof( UNICODE_NULL ) );
+
+ if ( (*MachineName)->Buffer != NULL ) {
+
+ (*MachineName)->MaximumLength = (USHORT)(WorkStationName->Length + sizeof( UNICODE_NULL ));
+ RtlCopyUnicodeString( *MachineName, WorkStationName );
+
+ } else {
+
+ RtlInitUnicodeString( *MachineName, NULL );
+ }
+ }
+ }
+
+ //
+ // Map status codes to prevent specific information from being
+ // released about this user.
+ //
+ switch (Status) {
+ case STATUS_WRONG_PASSWORD:
+ case STATUS_NO_SUCH_USER:
+
+ //
+ // sleep 3 seconds to "discourage" dictionary attacks.
+ // Don't worry about interactive logon dictionary attacks.
+ // They will be slow anyway.
+ //
+ if (LogonType != Interactive) {
+ Sleep( 3000 );
+ }
+
+ //
+ // This is for auditing. Make sure to clear it out before
+ // passing it out of LSA to the caller.
+ //
+
+ *SubStatus = Status;
+ Status = STATUS_LOGON_FAILURE;
+ break;
+
+ case STATUS_INVALID_LOGON_HOURS:
+ case STATUS_INVALID_WORKSTATION:
+ case STATUS_PASSWORD_EXPIRED:
+ case STATUS_ACCOUNT_DISABLED:
+ *SubStatus = Status;
+ Status = STATUS_ACCOUNT_RESTRICTION;
+ break;
+
+ //
+ // This shouldn't happen, but guard against it anyway.
+ //
+ case STATUS_ACCOUNT_RESTRICTION:
+ *SubStatus = STATUS_ACCOUNT_RESTRICTION;
+ break;
+
+ default:
+ break;
+
+ }
+
+
+ //
+ // Cleanup locally used resources
+ //
+
+ if ( Credential != NULL ) {
+ (*Lsa.FreeLsaHeap)( Credential );
+ }
+
+ if ( NlpUser != NULL ) {
+ MIDL_user_free( NlpUser );
+ }
+
+ if ( UserSid != NULL ) {
+ (*Lsa.FreeLsaHeap)( UserSid );
+ }
+
+
+ //
+ // Return status to the caller
+ //
+
+ return Status;
+
+}
+
+
+VOID
+LsaApLogonTerminated (
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to notify each authentication package when a logon
+ session terminates. A logon session terminates when the last token
+ referencing the logon session is deleted.
+
+Arguments:
+
+ LogonId - Is the logon ID that just logged off.
+
+Return Status:
+
+ None.
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PACTIVE_LOGON LogonEntry;
+ PACTIVE_LOGON *ActiveLogon;
+ NETLOGON_INTERACTIVE_INFO LogonInteractive;
+ PNETLOGON_INTERACTIVE_INFO LogonInteractivePointer;
+
+ //
+ // Find the entry and de-link it from the active logon table.
+ //
+
+ NlpLockActiveLogons();
+
+ if ( !NlpFindActiveLogon( LogonId, &ActiveLogon ) ) {
+ NlpUnlockActiveLogons();
+ return;
+ }
+
+ LogonEntry = *ActiveLogon;
+ *ActiveLogon = LogonEntry->Next;
+ NlpUnlockActiveLogons();
+
+ //
+ // Build the Logon Information structure.
+ //
+
+ LogonInteractive.Identity.LogonDomainName = LogonEntry->LogonDomainName;
+ LogonInteractive.Identity.ParameterControl = 0;
+ NEW_TO_OLD_LARGE_INTEGER(
+ LogonEntry->LogonId,
+ LogonInteractive.Identity.LogonId );
+ LogonInteractive.Identity.UserName = LogonEntry->UserName;
+ LogonInteractive.Identity.Workstation = NlpComputerName;
+
+ //
+ // If this entry was logged on via netlogon,
+ // tell netlogon the session is terminated.
+ //
+
+ if ( (LogonEntry->Flags & LOGON_BY_NETLOGON) && NlpNetlogonDllHandle != NULL ) {
+
+ //
+ // If netlogon isn't running,
+ // it isn't worth waiting around just to update the logon statistics.
+ // just return an error.
+ //
+
+ if ( !NlpNetlogonInitialized ) {
+ Status = STATUS_NETLOGON_NOT_STARTED; // Map to an error condition
+ goto Cleanup;
+ }
+
+
+ //
+ // Call Netlogon to log off.
+ //
+
+ LogonInteractivePointer = &LogonInteractive;
+
+ Status = (*NlpNetLogonSamLogoff) (
+ NULL, // Server name
+ NULL, // Computer name
+ NULL, // Authenticator
+ NULL, // ReturnAuthenticator
+ NetlogonInteractiveInformation,
+ (LPBYTE) &LogonInteractivePointer );
+
+ if ( !NT_SUCCESS(Status) ) {
+ KdPrint((
+ "MSV1_0: LsaApLogonTerminated:"
+ " Cannot logoff from Netlogon service %lX\n",
+ Status ));
+ }
+
+ //
+ // if we logged on via the cache,
+ // there are no logon statistics to update.
+ //
+
+ } else if ( LogonEntry->Flags & LOGON_BY_CACHE ) {
+
+ /* Nothing to do here */
+ KdPrint(( "MSV1_0: LsaApLogonTerminated:"
+ " User who logged on via cache has logged off.\n"));
+
+ //
+ // Otherwise just update the local logon statistics
+ //
+
+ } else {
+
+ ASSERT( NlpSamInitialized );
+
+ Status = MsvSamLogoff(
+ NlpSamDomainHandle,
+ NetlogonInteractiveInformation,
+ (LPBYTE) &LogonInteractive );
+
+ if ( !NT_SUCCESS(Status) ) {
+ KdPrint((
+ "MSV1_0: LsaApLogonTerminated:"
+ " Cannot update Sam Logoff statistics %lX\n",
+ Status ));
+ }
+ }
+
+Cleanup:
+ //
+ // Delete the credential.
+ //
+ // (Currently the LSA deletes all of the credentials before calling
+ // the authentication package. This line is added to be compatible
+ // with a more reasonable LSA.)
+ //
+
+ (VOID) NlpDeletePrimaryCredential( &LogonEntry->LogonId );
+
+ //
+ // Deallocate the now orphaned entry.
+ //
+
+ RtlFreeHeap( MspHeap, 0, LogonEntry );
+
+
+ //
+ // NB: We don't delete the logon session or credentials.
+ // That will be done by the LSA itself after we return.
+ //
+
+ return;
+
+}
diff --git a/private/lsa/msv1_0/nlnetapi.c b/private/lsa/msv1_0/nlnetapi.c
new file mode 100644
index 000000000..6d06b5895
--- /dev/null
+++ b/private/lsa/msv1_0/nlnetapi.c
@@ -0,0 +1,263 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nlnetapi.c
+
+Abstract:
+
+ This module loads Netapi.dll at runtime and sets up pointers to
+ the APIs called by Msv1_0.
+
+Author:
+
+ Dave Hart (DaveHart) 25-Mar-1992
+
+Environment:
+
+ User mode Win32 - msv1_0 authentication package DLL
+
+Revision History:
+
+ Dave Hart (DaveHart) 26-Mar-1992
+ Added RxNetUserPasswordSet.
+
+ Dave Hart (DaveHart) 30-May-1992
+ Removed NetRemoteComputerSupports, added NetApiBufferAllocate.
+
+--*/
+
+#include "msp.h"
+#include "nlp.h"
+
+typedef NTSTATUS
+ (*PI_NETNOTIFYNETLOGONDLLHANDLE) (
+ IN PHANDLE Role
+ );
+
+PI_NETNOTIFYNETLOGONDLLHANDLE pI_NetNotifyNetlogonDllHandle = NULL;
+
+
+
+VOID
+NlpLoadNetapiDll (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Uses Win32 LoadLibrary and GetProcAddress to get pointers to functions
+ in Netapi.dll that are called by Msv1_0.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None. If successful, NlpNetapiDllLoaded is set to TRUE and function
+ pointers are setup.
+
+
+--*/
+
+{
+ HANDLE hModule;
+
+ hModule = LoadLibraryA("netapi32");
+
+ if (NULL == hModule) {
+#if DBG
+ DbgPrint("Msv1_0: Unable to load netapi32.dll, Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+
+
+
+ NlpNetGetDCName =
+ (NET_API_STATUS (NET_API_FUNCTION *)(LPWSTR, LPWSTR, LPBYTE *))
+ GetProcAddress(hModule, "NetGetDCName");
+
+ if (NULL == NlpNetGetDCName) {
+#if DBG
+ DbgPrint("Msv1_0: Can't find entrypoint NetGetDCName in netapi32.dll.\n"
+ " Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+
+
+
+ NlpNetApiBufferFree =
+ (NET_API_STATUS (NET_API_FUNCTION *)(LPVOID))
+ GetProcAddress(hModule, "NetApiBufferFree");
+
+ if (NlpNetApiBufferFree == NULL) {
+#if DBG
+ DbgPrint("Msv1_0: Can't find entrypoint NetApiBufferFree in netapi32.dll.\n"
+ " Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+
+
+
+ NlpRxNetUserPasswordSet =
+ (NET_API_STATUS (NET_API_FUNCTION *)(LPWSTR, LPWSTR, LPWSTR, LPWSTR))
+ GetProcAddress(hModule, "RxNetUserPasswordSet");
+
+ if (NULL == NlpRxNetUserPasswordSet) {
+#if DBG
+ DbgPrint("Msv1_0: Can't find entrypoint RxNetUserPasswordSet in netapi32.dll.\n"
+ " Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+
+
+
+ NlpNetpApiStatusToNtStatus =
+ (NTSTATUS (*)(NET_API_STATUS))
+ GetProcAddress(hModule, "NetpApiStatusToNtStatus");
+
+ if (NlpNetpApiStatusToNtStatus == NULL) {
+#if DBG
+ DbgPrint("Msv1_0: Can't find entrypoint NetpApiStatusToNtStatus in netapi32.dll.\n"
+ " Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+
+
+
+ //
+ // Found all the functions needed, so indicate success.
+ //
+
+ NlpNetapiDllLoaded = TRUE;
+
+Cleanup:
+
+ return;
+
+}
+
+
+
+
+VOID
+NlpLoadNetlogonDll (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Uses Win32 LoadLibrary and GetProcAddress to get pointers to functions
+ in Netlogon.dll that are called by Msv1_0.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None. If successful, NlpNetlogonDllHandle is set to non-NULL and function
+ pointers are setup.
+
+
+--*/
+
+{
+ HANDLE hModule = NULL;
+
+
+
+ //
+ // Load netlogon.dll also.
+ //
+
+ hModule = LoadLibraryA("netlogon");
+
+ if (NULL == hModule) {
+#if DBG
+ DbgPrint("Msv1_0: Unable to load netlogon.dll, Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+
+
+
+ NlpNetLogonSamLogon = (PNETLOGON_SAM_LOGON_PROCEDURE)
+ GetProcAddress(hModule, "NetrLogonSamLogon");
+
+ if (NlpNetLogonSamLogon == NULL) {
+#if DBG
+ DbgPrint(
+ "Msv1_0: Can't find entrypoint NetrLogonSamLogon in netlogon.dll.\n"
+ " Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+
+
+
+ NlpNetLogonSamLogoff = (PNETLOGON_SAM_LOGOFF_PROCEDURE)
+ GetProcAddress(hModule, "NetrLogonSamLogoff");
+
+ if (NlpNetLogonSamLogoff == NULL) {
+#if DBG
+ DbgPrint(
+ "Msv1_0: Can't find entrypoint NetrLogonSamLogoff in netlogon.dll.\n"
+ " Win32 error %d.\n", GetLastError());
+#endif
+ goto Cleanup;
+ }
+
+ //
+ // Find the address of the I_NetNotifyNetlogonDllHandle procedure.
+ // This is an optional procedure so don't complain about its absence.
+ //
+
+ pI_NetNotifyNetlogonDllHandle = (PI_NETNOTIFYNETLOGONDLLHANDLE)
+ GetProcAddress( hModule, "I_NetNotifyNetlogonDllHandle" );
+
+
+
+
+ //
+ // Found all the functions needed, so indicate success.
+ //
+
+ NlpNetlogonDllHandle = hModule;
+ hModule = NULL;
+
+ //
+ // Notify Netlogon that we've loaded it.
+ //
+
+ if ( pI_NetNotifyNetlogonDllHandle != NULL ) {
+ (VOID) (*pI_NetNotifyNetlogonDllHandle)( &NlpNetlogonDllHandle );
+ }
+
+Cleanup:
+ if ( hModule != NULL ) {
+ FreeLibrary( hModule );
+ }
+
+ return;
+
+}
diff --git a/private/lsa/msv1_0/nlp.c b/private/lsa/msv1_0/nlp.c
new file mode 100644
index 000000000..0d7d2da8a
--- /dev/null
+++ b/private/lsa/msv1_0/nlp.c
@@ -0,0 +1,1822 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nlp.c
+
+Abstract:
+
+ This file is the contains private routines which support
+ for the LAN Manager portions of the MSV1_0 authentication package.
+
+Author:
+
+ Cliff Van Dyke 29-Apr-1991
+
+Revision History:
+
+--*/
+
+#include "msp.h"
+#include "nlp.h"
+#include "nlpcache.h"
+#include <stdlib.h>
+
+
+VOID
+NlpPutString(
+ IN PUNICODE_STRING OutString,
+ IN PUNICODE_STRING InString,
+ IN PUCHAR *Where
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the InString string to the memory pointed to by
+ the Where parameter, and fixes the OutString string to point to that
+ new copy.
+
+Parameters:
+
+ OutString - A pointer to a destination NT string
+
+ InString - A pointer to an NT string to be copied
+
+ Where - A pointer to space to put the actual string for the
+ OutString. The pointer is adjusted to point to the first byte
+ following the copied string.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT( OutString != NULL );
+ ASSERT( InString != NULL );
+ ASSERT( Where != NULL && *Where != NULL);
+ ASSERT( *Where == ROUND_UP_POINTER( *Where, sizeof(WCHAR) ) );
+#ifdef notdef
+ KdPrint(("NlpPutString: %ld %Z\n", InString->Length, InString ));
+ KdPrint((" InString: %lx %lx OutString: %lx Where: %lx\n", InString,
+ InString->Buffer, OutString, *Where ));
+#endif
+
+ if ( InString->Length > 0 ) {
+
+ OutString->Buffer = (PWCH) *Where;
+ OutString->MaximumLength = (USHORT)(InString->Length + sizeof(WCHAR));
+
+ RtlCopyUnicodeString( OutString, InString );
+
+ *Where += InString->Length;
+// *((WCHAR *)(*Where)) = L'\0';
+ *(*Where) = '\0';
+ *(*Where + 1) = '\0';
+ *Where += 2;
+
+ } else {
+ RtlInitUnicodeString(OutString, NULL);
+ }
+#ifdef notdef
+ KdPrint((" OutString: %ld %lx\n", OutString->Length, OutString->Buffer));
+#endif
+
+ return;
+}
+
+
+VOID
+NlpInitClientBuffer(
+ OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ IN PLSA_CLIENT_REQUEST ClientRequest
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes a ClientBufferDescriptor to known values.
+ This routine must be called before any of the other routines that use
+ the ClientBufferDescriptor.
+
+Parameters:
+
+ ClientBufferDesc - Descriptor of a buffer allocated in the client's
+ address space.
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Fill in a pointer to the ClientRequest and zero the rest.
+ //
+
+ ClientBufferDesc->ClientRequest = ClientRequest;
+ ClientBufferDesc->UserBuffer = NULL;
+ ClientBufferDesc->MsvBuffer = NULL;
+ ClientBufferDesc->StringOffset = 0;
+ ClientBufferDesc->TotalSize = 0;
+
+}
+
+
+NTSTATUS
+NlpAllocateClientBuffer(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ IN ULONG FixedSize,
+ IN ULONG TotalSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates a buffer in the clients address space.
+ It also allocates a mirror buffer in MSV's address space.
+
+ The data will be constructed in the MSV's address space then 'flushed'
+ into the client's address space.
+
+Parameters:
+
+ ClientBufferDesc - Descriptor of a buffer allocated in the client's
+ address space.
+
+ FixedSize - The size in bytes of the fixed portion of the buffer.
+
+ TotalSize - The size in bytes of the entire buffer.
+
+Return Values:
+
+ Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Allocate the Mirror buffer.
+ //
+
+ ASSERT( ClientBufferDesc->MsvBuffer == NULL );
+ ClientBufferDesc->MsvBuffer = RtlAllocateHeap( MspHeap, 0, TotalSize );
+
+ if ( ClientBufferDesc->MsvBuffer == NULL ) {
+ return STATUS_NO_MEMORY;
+ }
+
+
+ //
+ // Allocate the client's buffer
+ //
+
+ ASSERT( ClientBufferDesc->UserBuffer == NULL );
+ Status = (*Lsa.AllocateClientBuffer)(
+ ClientBufferDesc->ClientRequest,
+ TotalSize,
+ (PVOID *)&ClientBufferDesc->UserBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ ClientBufferDesc->UserBuffer = NULL;
+ NlpFreeClientBuffer( ClientBufferDesc );
+ return Status;
+ }
+
+ //
+ // Return
+ //
+
+ ClientBufferDesc->StringOffset = FixedSize;
+ ClientBufferDesc->TotalSize = TotalSize;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+NlpFlushClientBuffer(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ OUT PVOID* UserBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ Copy the Mirror Buffer into the Client's address space.
+
+Parameters:
+
+ ClientBufferDesc - Descriptor of a buffer allocated in the client's
+ address space.
+
+ UserBuffer - If successful, returns a pointer to the user's buffer.
+ (The caller is now resposible for deallocating the buffer.)
+
+Return Values:
+
+ Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Copy the data to the client's address space.
+ //
+
+ Status = (*Lsa.CopyToClientBuffer)(
+ ClientBufferDesc->ClientRequest,
+ ClientBufferDesc->TotalSize,
+ ClientBufferDesc->UserBuffer,
+ ClientBufferDesc->MsvBuffer );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // Mark that we're no longer responsible for the client's buffer.
+ //
+
+ *UserBuffer = (PVOID) ClientBufferDesc->UserBuffer;
+ ClientBufferDesc->UserBuffer = NULL;
+
+ //
+ // Free the mirror buffer
+ //
+
+ NlpFreeClientBuffer( ClientBufferDesc );
+
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+NlpFreeClientBuffer(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc
+ )
+
+/*++
+
+Routine Description:
+
+ Free any Mirror Buffer or Client buffer.
+
+Parameters:
+
+ ClientBufferDesc - Descriptor of a buffer allocated in the client's
+ address space.
+
+Return Values:
+
+ None
+
+--*/
+
+{
+
+ //
+ // Free the mirror buffer.
+ //
+
+ if ( ClientBufferDesc->MsvBuffer != NULL ) {
+ RtlFreeHeap( MspHeap, 0, ClientBufferDesc->MsvBuffer );
+ ClientBufferDesc->MsvBuffer = NULL;
+ }
+
+ //
+ // Free the Client's buffer
+ //
+
+ if ( ClientBufferDesc->UserBuffer != NULL ) {
+ (VOID) (*Lsa.FreeClientBuffer)( ClientBufferDesc->ClientRequest,
+ ClientBufferDesc->UserBuffer );
+ ClientBufferDesc->UserBuffer = NULL;
+ }
+
+}
+
+
+VOID
+NlpPutClientString(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ IN PUNICODE_STRING OutString,
+ IN PUNICODE_STRING InString
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the InString string to the memory pointed to by
+ ClientBufferDesc->StringOffset, and fixes the OutString string to point
+ to that new copy.
+
+
+Parameters:
+
+ ClientBufferDesc - Descriptor of a buffer allocated in the client's
+ address space.
+
+ InString - A pointer to an NT string to be copied
+
+ OutString - A pointer to a destination NT string. This string structure
+ is in the "Mirror" allocated buffer.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+
+ //
+ // Ensure our caller passed good data.
+ //
+
+ ASSERT( OutString != NULL );
+ ASSERT( InString != NULL );
+ ASSERT( COUNT_IS_ALIGNED( ClientBufferDesc->StringOffset, sizeof(WCHAR)) );
+ ASSERT( (LPBYTE)OutString >= ClientBufferDesc->MsvBuffer );
+ ASSERT( (LPBYTE)OutString <
+ ClientBufferDesc->MsvBuffer + ClientBufferDesc->TotalSize - sizeof(UNICODE_STRING) );
+
+ ASSERT( ClientBufferDesc->StringOffset + InString->Length + sizeof(WCHAR) <=
+ ClientBufferDesc->TotalSize );
+
+#ifdef notdef
+ KdPrint(("NlpPutClientString: %ld %Z\n", InString->Length, InString ));
+ KdPrint((" Orig: UserBuffer: %lx Offset: 0x%lx TotalSize: 0x%lx\n",
+ ClientBufferDesc->UserBuffer,
+ ClientBufferDesc->StringOffset,
+ ClientBufferDesc->TotalSize ));
+#endif
+
+ //
+ // Build a string structure and copy the text to the Mirror buffer.
+ //
+
+ if ( InString->Length > 0 ) {
+
+ //
+ // Copy the string (Add a zero character)
+ //
+
+ RtlCopyMemory(
+ ClientBufferDesc->MsvBuffer + ClientBufferDesc->StringOffset,
+ InString->Buffer,
+ InString->Length );
+
+ // Do one byte at a time since some callers don't pass in an even
+ // InString->Length
+ *(ClientBufferDesc->MsvBuffer + ClientBufferDesc->StringOffset +
+ InString->Length) = '\0';
+ *(ClientBufferDesc->MsvBuffer + ClientBufferDesc->StringOffset +
+ InString->Length+1) = '\0';
+
+ //
+ // Build the string structure to point to the data in the client's
+ // address space.
+ //
+
+ OutString->Buffer = (PWSTR)(ClientBufferDesc->UserBuffer +
+ ClientBufferDesc->StringOffset);
+ OutString->Length = InString->Length;
+ OutString->MaximumLength = OutString->Length + sizeof(WCHAR);
+
+ //
+ // Adjust the offset to past the newly copied string.
+ //
+
+ ClientBufferDesc->StringOffset += OutString->MaximumLength;
+
+ } else {
+ RtlInitUnicodeString(OutString, NULL);
+ }
+
+#ifdef notdef
+ KdPrint((" New: Offset: 0x%lx StringStart: %lx\n",
+ ClientBufferDesc->StringOffset,
+ OutString->Buffer ));
+#endif
+
+ return;
+
+}
+
+
+VOID
+NlpMakeRelativeString(
+ IN PUCHAR BaseAddress,
+ IN OUT PUNICODE_STRING String
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts the buffer address in the specified string to
+ be a byte offset from BaseAddress.
+
+Parameters:
+
+ BaseAddress - A pointer to make the destination address relative to.
+
+ String - A pointer to a NT string to make relative.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT( BaseAddress != NULL );
+ ASSERT( String != NULL );
+ ASSERT( sizeof(ULONG) == sizeof(String->Buffer) );
+
+ if ( String->Buffer != NULL ) {
+ *((PULONG)(&String->Buffer)) =
+ (PUCHAR)String->Buffer - (PUCHAR)BaseAddress;
+ }
+
+ return;
+}
+
+
+VOID
+NlpRelativeToAbsolute(
+ IN PVOID BaseAddress,
+ IN OUT PULONG RelativeValue
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts the byte offset from BaseAddress to be an
+ absolute address.
+
+Parameters:
+
+ BaseAddress - A pointer the destination address is relative to.
+
+ RelativeValue - A pointer to a relative value to make absolute.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT( BaseAddress != NULL );
+ ASSERT( RelativeValue != NULL );
+
+ if ( *((PUCHAR *)RelativeValue) != NULL ) {
+ *RelativeValue = (ULONG)((PUCHAR)BaseAddress + (*RelativeValue));
+ }
+
+ return;
+}
+
+
+BOOLEAN
+NlpFindActiveLogon(
+ IN PLUID LogonId,
+ OUT PACTIVE_LOGON **ActiveLogon
+ )
+
+/*++
+
+Routine Description:
+
+ This routine finds the specified Logon Id in the ActiveLogon table.
+ It returns a boolean indicating whether the Logon Id exists in the
+ ActiveLogon Table. If so, this routine also returns a pointer to a
+ pointer to the appropriate entry in the table. If not, this routine
+ returns a pointer to where such an entry would be inserted in the table.
+
+ This routine must be called with the NlpActiveLogonLock locked.
+
+Parameters:
+
+ LogonId - The LogonId of the logon to find in the table.
+
+ ActiveLogon - If the specified logon Id exists, returns a pointer to a
+ pointer to the appropriate entry in the table. Otherwise,
+ returns a pointer to where such an entry would be inserted in the
+ table.
+
+Return Values:
+
+ TRUE - The specified LogonId already exists in the table.
+
+ FALSE - The specified LogonId does not exist in the table.
+
+--*/
+
+{
+ PACTIVE_LOGON *Logon;
+
+ //
+ // Loop through the table looking for this particular LogonId.
+ //
+
+ for( Logon = &NlpActiveLogons; *Logon != NULL; Logon = &((*Logon)->Next) ) {
+ if (RtlCompareMemory( &(*Logon)->LogonId, LogonId, sizeof(*LogonId))
+ == sizeof(*LogonId) ) {
+
+ *ActiveLogon = Logon;
+ return TRUE;
+ }
+ }
+
+ //
+ // By returning a pointer to the NULL at the end of the list, we
+ // are forcing new entries to be placed at the end. The list is
+ // thereby maintained in the order that the logon occurred.
+ // MsV1_0EnumerateUsers relies on this behavior.
+ //
+
+ *ActiveLogon = Logon;
+ return FALSE;
+}
+
+
+ULONG
+NlpCountActiveLogon(
+ IN PUNICODE_STRING LogonDomainName,
+ IN PUNICODE_STRING UserName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine counts the number of time a particular user is logged on
+ in the Active Logon Table.
+
+Parameters:
+
+ LogonDomainName - Domain in which this user account is defined.
+
+ UserName - The user name to count the active logons for.
+
+Return Values:
+
+ The count of active logons for the specified user.
+
+--*/
+
+{
+ PACTIVE_LOGON Logon;
+ ULONG LogonCount = 0;
+
+
+ //
+ // Loop through the table looking for this particular LogonId.
+ //
+
+ NlpLockActiveLogons();
+
+ for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {
+
+ if(RtlEqualUnicodeString( UserName, &Logon->UserName, (BOOLEAN) TRUE) &&
+ RtlEqualDomainName(LogonDomainName,&Logon->LogonDomainName )){
+ LogonCount ++;
+ }
+
+ }
+
+ NlpUnlockActiveLogons();
+
+ return LogonCount;
+}
+
+
+
+NTSTATUS
+NlpAllocateInteractiveProfile (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ OUT PMSV1_0_INTERACTIVE_PROFILE *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 NlpUser
+ )
+
+/*++
+
+Routine Description:
+
+ This allocates and fills in the clients interactive profile.
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ ProfileBuffer - Is used to return the address of the profile
+ buffer in the client process. This routine is
+ responsible for allocating and returning the profile buffer
+ within the client process. However, if the caller subsequently
+ encounters an error which prevents a successful logon, then
+ then it will take care of deallocating the buffer. This
+ buffer is allocated with the AllocateClientBuffer() service.
+
+ ProfileBufferSize - Receives the Size (in bytes) of the
+ returned profile buffer.
+
+ NlpUser - Contains the validation information which is
+ to be copied in the ProfileBuffer.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+--*/
+
+{
+ NTSTATUS Status;
+ CLIENT_BUFFER_DESC ClientBufferDesc;
+ PMSV1_0_INTERACTIVE_PROFILE LocalProfileBuffer;
+
+ //
+ // Alocate the profile buffer to return to the client
+ //
+
+ NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
+
+ *ProfileBuffer = NULL;
+
+ *ProfileBufferSize = sizeof(MSV1_0_INTERACTIVE_PROFILE) +
+ NlpUser->LogonScript.Length + sizeof(WCHAR) +
+ NlpUser->HomeDirectory.Length + sizeof(WCHAR) +
+ NlpUser->HomeDirectoryDrive.Length + sizeof(WCHAR) +
+ NlpUser->FullName.Length + sizeof(WCHAR) +
+ NlpUser->ProfilePath.Length + sizeof(WCHAR) +
+ NlpUser->LogonServer.Length + sizeof(WCHAR);
+
+ Status = NlpAllocateClientBuffer( &ClientBufferDesc,
+ sizeof(MSV1_0_INTERACTIVE_PROFILE),
+ *ProfileBufferSize );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ LocalProfileBuffer = (PMSV1_0_INTERACTIVE_PROFILE) ClientBufferDesc.MsvBuffer;
+
+ //
+ // Copy the scalar fields into the profile buffer.
+ //
+
+ LocalProfileBuffer->MessageType = MsV1_0InteractiveProfile;
+ LocalProfileBuffer->LogonCount = NlpUser->LogonCount;
+ LocalProfileBuffer->BadPasswordCount= NlpUser->BadPasswordCount;
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->LogonTime,
+ LocalProfileBuffer->LogonTime );
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->LogoffTime,
+ LocalProfileBuffer->LogoffTime );
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->KickOffTime,
+ LocalProfileBuffer->KickOffTime );
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordLastSet,
+ LocalProfileBuffer->PasswordLastSet );
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordCanChange,
+ LocalProfileBuffer->PasswordCanChange );
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordMustChange,
+ LocalProfileBuffer->PasswordMustChange );
+ LocalProfileBuffer->UserFlags = NlpUser->UserFlags;
+
+ //
+ // Copy the Unicode strings into the profile buffer.
+ //
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfileBuffer->LogonScript,
+ &NlpUser->LogonScript );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfileBuffer->HomeDirectory,
+ &NlpUser->HomeDirectory );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfileBuffer->HomeDirectoryDrive,
+ &NlpUser->HomeDirectoryDrive );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfileBuffer->FullName,
+ &NlpUser->FullName );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfileBuffer->ProfilePath,
+ &NlpUser->ProfilePath );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfileBuffer->LogonServer,
+ &NlpUser->LogonServer );
+
+ //
+ // Flush the buffer to the client's address space.
+ //
+
+ Status = NlpFlushClientBuffer( &ClientBufferDesc,
+ (PVOID *) ProfileBuffer );
+
+Cleanup:
+
+ //
+ // If the copy wasn't successful,
+ // cleanup resources we would have returned to the caller.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlpFreeClientBuffer( &ClientBufferDesc );
+ }
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+NlpAllocateNetworkProfile (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ OUT PMSV1_0_LM20_LOGON_PROFILE *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 NlpUser,
+ IN ULONG ParameterControl
+ )
+
+/*++
+
+Routine Description:
+
+ This allocates and fills in the clients network profile.
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ ProfileBuffer - Is used to return the address of the profile
+ buffer in the client process. This routine is
+ responsible for allocating and returning the profile buffer
+ within the client process. However, if the caller subsequently
+ encounters an error which prevents a successful logon, then
+ then it will take care of deallocating the buffer. This
+ buffer is allocated with the AllocateClientBuffer() service.
+
+ ProfileBufferSize - Receives the Size (in bytes) of the
+ returned profile buffer.
+
+ NlpUser - Contains the validation information which is
+ to be copied in the ProfileBuffer. Will be NULL to indicate a
+ NULL session.
+
+ ParameterControl - Contains the flags passed by the caller
+ of LsaLogonUser.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ CLIENT_BUFFER_DESC ClientBufferDesc;
+ PMSV1_0_LM20_LOGON_PROFILE LocalProfile;
+
+ //
+ // Alocate the profile buffer to return to the client
+ //
+
+ NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
+
+ *ProfileBuffer = NULL;
+ *ProfileBufferSize = sizeof(MSV1_0_LM20_LOGON_PROFILE);
+
+ if ( NlpUser != NULL ) {
+ *ProfileBufferSize += NlpUser->LogonDomainName.Length + sizeof(WCHAR) +
+ NlpUser->LogonServer.Length + sizeof(WCHAR) +
+ NlpUser->HomeDirectoryDrive.Length + sizeof(WCHAR);
+ }
+
+
+ Status = NlpAllocateClientBuffer( &ClientBufferDesc,
+ sizeof(MSV1_0_LM20_LOGON_PROFILE),
+ *ProfileBufferSize );
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto Cleanup;
+ }
+
+ LocalProfile = (PMSV1_0_LM20_LOGON_PROFILE) ClientBufferDesc.MsvBuffer;
+ LocalProfile->MessageType = MsV1_0Lm20LogonProfile;
+
+
+ //
+ // For a NULL session, return a constant profile buffer
+ //
+
+ if ( NlpUser == NULL ) {
+
+ LocalProfile->KickOffTime.HighPart = 0x7FFFFFFF;
+ LocalProfile->KickOffTime.LowPart = 0xFFFFFFFF;
+ LocalProfile->LogoffTime.HighPart = 0x7FFFFFFF;
+ LocalProfile->LogoffTime.LowPart = 0xFFFFFFFF;
+ LocalProfile->UserFlags = 0;
+ RtlZeroMemory( LocalProfile->UserSessionKey,
+ sizeof(LocalProfile->UserSessionKey));
+ RtlZeroMemory( LocalProfile->LanmanSessionKey,
+ sizeof(LocalProfile->LanmanSessionKey));
+ RtlInitUnicodeString( &LocalProfile->LogonDomainName, NULL );
+ RtlInitUnicodeString( &LocalProfile->LogonServer, NULL );
+ RtlInitUnicodeString( &LocalProfile->UserParameters, NULL );
+
+
+ //
+ // For non-null sessions,
+ // fill in the profile buffer.
+ //
+
+ } else {
+
+ //
+ // Copy the individual scalar fields into the profile buffer.
+ //
+
+ if ((ParameterControl & MSV1_0_RETURN_PASSWORD_EXPIRY) != 0) {
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordMustChange,
+ LocalProfile->LogoffTime);
+ } else {
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->LogoffTime,
+ LocalProfile->LogoffTime);
+ }
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->KickOffTime,
+ LocalProfile->KickOffTime);
+ LocalProfile->UserFlags = NlpUser->UserFlags;
+
+ RtlCopyMemory( LocalProfile->UserSessionKey,
+ &NlpUser->UserSessionKey,
+ sizeof(LocalProfile->UserSessionKey) );
+
+ ASSERT( SAMINFO_LM_SESSION_KEY_SIZE ==
+ sizeof(LocalProfile->LanmanSessionKey) );
+ RtlCopyMemory(
+ LocalProfile->LanmanSessionKey,
+ &NlpUser->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
+ SAMINFO_LM_SESSION_KEY_SIZE );
+
+
+ //
+ // Copy the Unicode strings into the profile buffer.
+ //
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfile->LogonDomainName,
+ &NlpUser->LogonDomainName );
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfile->LogonServer,
+ &NlpUser->LogonServer );
+
+ //
+ // Kludge: Pass back UserParameters in HomeDirectoryDrive since we
+ // can't change the NETLOGON_VALIDATION_SAM_INFO structure between
+ // releases NT 1.0 and NT 1.0A. HomeDirectoryDrive was NULL for release 1.0A
+ // so we'll use that field.
+ //
+
+ NlpPutClientString( &ClientBufferDesc,
+ &LocalProfile->UserParameters,
+ &NlpUser->HomeDirectoryDrive );
+
+ }
+
+ //
+ // Flush the buffer to the client's address space.
+ //
+
+ Status = NlpFlushClientBuffer( &ClientBufferDesc,
+ ProfileBuffer );
+
+Cleanup:
+
+ //
+ // If the copy wasn't successful,
+ // cleanup resources we would have returned to the caller.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ NlpFreeClientBuffer( &ClientBufferDesc );
+ }
+
+ return Status;
+
+}
+
+
+PSID
+NlpMakeDomainRelativeSid(
+ IN PSID DomainId,
+ IN ULONG RelativeId
+ )
+
+/*++
+
+Routine Description:
+
+ Given a domain Id and a relative ID create the corresponding SID allocated
+ from the LSA heap.
+
+Arguments:
+
+ DomainId - The template SID to use.
+
+ RelativeId - The relative Id to append to the DomainId.
+
+Return Value:
+
+ Sid - Returns a pointer to a buffer allocated from the LsaHeap
+ containing the resultant Sid.
+
+--*/
+{
+ UCHAR DomainIdSubAuthorityCount;
+ ULONG Size;
+ PSID Sid;
+
+ //
+ // Allocate a Sid which has one more sub-authority than the domain ID.
+ //
+
+ DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId ));
+ Size = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1);
+
+ if ((Sid = (*Lsa.AllocateLsaHeap)( Size )) == NULL ) {
+ return NULL;
+ }
+
+ //
+ // Initialize the new SID to have the same inital value as the
+ // domain ID.
+ //
+
+ if ( !NT_SUCCESS( RtlCopySid( Size, Sid, DomainId ) ) ) {
+ (*Lsa.FreeLsaHeap)( Sid );
+ return NULL;
+ }
+
+ //
+ // Adjust the sub-authority count and
+ // add the relative Id unique to the newly allocated SID
+ //
+
+ (*(RtlSubAuthorityCountSid( Sid ))) ++;
+ *RtlSubAuthoritySid( Sid, DomainIdSubAuthorityCount ) = RelativeId;
+
+
+ return Sid;
+}
+
+
+
+PSID
+NlpCopySid(
+ IN PSID * Sid
+ )
+
+/*++
+
+Routine Description:
+
+ Given a SID allocatees space for a new SID from the LSA heap and copies
+ the original SID.
+
+Arguments:
+
+ Sid - The original SID.
+
+Return Value:
+
+ Sid - Returns a pointer to a buffer allocated from the LsaHeap
+ containing the resultant Sid.
+
+--*/
+{
+ PSID NewSid;
+ ULONG Size;
+
+ Size = RtlLengthSid( Sid );
+
+
+
+ if ((NewSid = (*Lsa.AllocateLsaHeap)( Size )) == NULL ) {
+ return NULL;
+ }
+
+
+ if ( !NT_SUCCESS( RtlCopySid( Size, NewSid, Sid ) ) ) {
+ (*Lsa.FreeLsaHeap)( NewSid );
+ return NULL;
+ }
+
+
+ return NewSid;
+}
+
+
+
+NTSTATUS
+NlpMakeTokenInformationV1(
+ IN PNETLOGON_VALIDATION_SAM_INFO2 NlpUser,
+ OUT PLSA_TOKEN_INFORMATION_V1 *TokenInformation
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine makes copies of all the pertinent information from the
+ NlpUser and generates a LSA_TOKEN_INFORMATION_V1 data
+ structure.
+
+Arguments:
+
+
+ NlpUser - Contains the validation information which is
+ to be copied into the TokenInformation.
+
+ TokenInformation - Returns a pointer to a properly Version 1 token
+ information structures. The structure and individual fields are
+ allocated properly as described in ntlsa.h.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSA_TOKEN_INFORMATION_V1 V1;
+ ULONG Size, i;
+
+
+
+ //
+ // Allocate the structure itself
+ //
+
+ Size = (ULONG)sizeof(LSA_TOKEN_INFORMATION_V1);
+ V1 = (*Lsa.AllocateLsaHeap)( Size );
+ if ( V1 == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ V1->User.User.Sid = NULL;
+ V1->Groups = NULL;
+ V1->PrimaryGroup.PrimaryGroup = NULL;
+ OLD_TO_NEW_LARGE_INTEGER( NlpUser->KickOffTime, V1->ExpirationTime );
+
+
+ //
+ // Make a copy of the user SID (a required field)
+ //
+
+ V1->User.User.Attributes = 0;
+
+
+ //
+ // If this is a server logon, build a more limited token information
+ // structure.
+ //
+
+ if ((NlpUser->UserFlags & LOGON_SERVER_TRUST_ACCOUNT) == 0)
+ {
+
+ //
+ // Allocate an array to hold the groups
+ //
+
+ Size = ( (ULONG)sizeof(TOKEN_GROUPS)
+ + (NlpUser->GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES))
+ - (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES))
+ );
+
+ //
+ // If there are extra SIDs, add space for them
+ //
+
+ if (NlpUser->UserFlags & LOGON_EXTRA_SIDS) {
+ Size += NlpUser->SidCount * (ULONG)sizeof(SID_AND_ATTRIBUTES);
+ }
+
+
+ V1->Groups = (*Lsa.AllocateLsaHeap)( Size );
+
+ if ( V1->Groups == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ V1->Groups->GroupCount = 0;
+
+ //
+ // Start copying SIDs into the structure
+ //
+
+
+
+ //
+ // If the UserId is non-zero, then it contians the users RID.
+ //
+
+ if ( NlpUser->UserId ) {
+ V1->User.User.Sid =
+ NlpMakeDomainRelativeSid( NlpUser->LogonDomainId,
+ NlpUser->UserId );
+
+ if( V1->User.User.Sid == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ }
+
+ //
+ // Make a copy of the primary group (a required field).
+ //
+
+
+ V1->PrimaryGroup.PrimaryGroup = NlpMakeDomainRelativeSid(
+ NlpUser->LogonDomainId,
+ NlpUser->PrimaryGroupId );
+
+ if ( V1->PrimaryGroup.PrimaryGroup == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+
+
+
+
+ //
+ // Copy over all the groups passed as RIDs
+ //
+
+ for ( i=0; i < NlpUser->GroupCount; i++ ) {
+
+ V1->Groups->Groups[i].Attributes = NlpUser->GroupIds[i].Attributes;
+
+ V1->Groups->Groups[i].Sid = NlpMakeDomainRelativeSid(
+ NlpUser->LogonDomainId,
+ NlpUser->GroupIds[i].RelativeId );
+
+ if( V1->Groups->Groups[i].Sid == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ V1->Groups->GroupCount++;
+ }
+
+
+ //
+ // Add in the extra SIDs
+ //
+
+ if (NlpUser->UserFlags & LOGON_EXTRA_SIDS) {
+
+ ULONG index = 0;
+ //
+ // If the user SID wasn't passed as a RID, it is the first
+ // SID.
+ //
+
+ if ( !V1->User.User.Sid ) {
+ if ( NlpUser->SidCount <= index ) {
+
+ Status = STATUS_INSUFFICIENT_LOGON_INFO;
+ goto Cleanup;
+ }
+ V1->User.User.Sid = NlpCopySid( NlpUser->ExtraSids[index].Sid );
+
+ if (!V1->User.User.Sid) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ index++;
+ }
+
+ //
+ // Copy over all additional SIDs as groups.
+ //
+
+ for ( ; index < NlpUser->SidCount; index++ ) {
+
+ V1->Groups->Groups[V1->Groups->GroupCount].Attributes =
+ NlpUser->ExtraSids[index].Attributes;
+
+ if (! (V1->Groups->Groups[V1->Groups->GroupCount].Sid =
+ NlpCopySid(NlpUser->ExtraSids[index].Sid ) ) ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+
+
+ V1->Groups->GroupCount++;
+ }
+ }
+
+ }
+ else
+ {
+ SID ServerLogonSid = {SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_SERVER_LOGON_RID };
+ ULONG SidSize;
+
+ //
+ // Built the server logon token. This is similar to an anonymous
+ // logon token but it has a SERVER LOGON SID instead of an
+ // ANONYMOUS LOGON sid
+ //
+
+ SidSize = RtlLengthSid(&ServerLogonSid);
+
+ V1->User.User.Sid = (*Lsa.AllocateLsaHeap)( SidSize );
+ if (V1->User.User.Sid == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory(
+ V1->User.User.Sid,
+ &ServerLogonSid,
+ SidSize
+ );
+
+ //
+ // Build the primary group to be the same
+ //
+
+ V1->PrimaryGroup.PrimaryGroup = (*Lsa.AllocateLsaHeap)( SidSize );
+ if (V1->PrimaryGroup.PrimaryGroup == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ RtlCopyMemory(
+ V1->PrimaryGroup.PrimaryGroup,
+ &ServerLogonSid,
+ SidSize
+ );
+
+ }
+
+ if (!V1->User.User.Sid) {
+
+ Status = STATUS_INSUFFICIENT_LOGON_INFO;
+ goto Cleanup;
+ }
+
+ //
+ // There are no default privileges supplied.
+ // We don't have an explicit owner SID.
+ // There is no default DACL.
+ //
+
+ V1->Privileges = NULL;
+ V1->Owner.Owner = NULL;
+ V1->DefaultDacl.DefaultDacl = NULL;
+
+ //
+ // Return the Validation Information to the caller.
+ //
+
+ *TokenInformation = V1;
+ return STATUS_SUCCESS;
+
+ //
+ // Deallocate any memory we've allocated
+ //
+
+Cleanup:
+ if ( V1->User.User.Sid != NULL ) {
+ (*Lsa.FreeLsaHeap)( V1->User.User.Sid );
+ }
+
+ if ( V1->Groups != NULL ) {
+
+ for ( i=0; i < V1->Groups->GroupCount; i++ ) {
+ (*Lsa.FreeLsaHeap)( V1->Groups->Groups[i].Sid );
+ }
+
+ (*Lsa.FreeLsaHeap)( V1->Groups );
+ }
+
+ if ( V1->PrimaryGroup.PrimaryGroup != NULL ) {
+ (*Lsa.FreeLsaHeap)( V1->PrimaryGroup.PrimaryGroup );
+ }
+
+ (*Lsa.FreeLsaHeap)( V1 );
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlpMakePrimaryCredential(
+ IN PUNICODE_STRING LogonDomainName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING CleartextPassword,
+ OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
+ OUT PULONG CredentialSize
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine makes a primary credential for the given user nam and
+ password.
+
+Arguments:
+
+ LogonDomainName - Is a string representing the domain in which the user's
+ account is defined.
+
+ UserName - Is a string representing the user's account name. The
+ name may be up to 255 characters long. The name is treated case
+ insensitive.
+
+ CleartextPassword - Is a string containing the user's cleartext password.
+ The password may be up to 255 characters long and contain any
+ UNICODE value.
+
+ CredentialBuffer - Returns a pointer to the specified credential allocated
+ on the LsaHeap. It is the callers responsibility to deallocate
+ this credential.
+
+ CredentialSize - the size of the allocated credential buffer (in bytes).
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+
+--*/
+
+{
+ PMSV1_0_PRIMARY_CREDENTIAL Credential;
+ NTSTATUS Status;
+ PUCHAR Where;
+ CHAR LmPassword[LM20_PWLEN+1];
+ BOOLEAN LmPasswordPresent;
+ STRING AnsiCleartextPassword;
+
+
+ //
+ // Compute the Ansi version to the Cleartext password.
+ //
+ // The Ansi version of the Cleartext password is at most 14 bytes long,
+ // exists in a trailing zero filled 15 byte buffer,
+ // is uppercased.
+ //
+
+ AnsiCleartextPassword.Buffer = LmPassword;
+ AnsiCleartextPassword.MaximumLength = sizeof(LmPassword);
+
+ RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
+
+ Status = RtlUpcaseUnicodeStringToOemString(
+ &AnsiCleartextPassword,
+ CleartextPassword,
+ (BOOLEAN) FALSE );
+
+ if ( !NT_SUCCESS(Status) ) {
+ RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
+ AnsiCleartextPassword.Length = 0;
+ LmPasswordPresent = FALSE;
+ } else {
+
+ LmPasswordPresent = TRUE;
+ }
+
+ //
+ // Build the credential
+ //
+
+ *CredentialSize = sizeof(MSV1_0_PRIMARY_CREDENTIAL) +
+ LogonDomainName->Length + sizeof(WCHAR) +
+ UserName->Length + sizeof(WCHAR);
+
+ Credential = (*Lsa.AllocateLsaHeap)( *CredentialSize );
+
+ if ( Credential == NULL ) {
+ KdPrint(("MSV1_0: NlpMakePrimaryCredential: No memory %ld\n",
+ *CredentialSize ));
+ return STATUS_QUOTA_EXCEEDED;
+ }
+
+
+ //
+ // Put the LogonDomainName into the Credential Buffer.
+ //
+
+ Where = (PUCHAR)(Credential + 1);
+
+ NlpPutString( &Credential->LogonDomainName, LogonDomainName, &Where );
+
+
+ //
+ // Put the UserName into the Credential Buffer.
+ //
+
+ NlpPutString( &Credential->UserName, UserName, &Where );
+
+
+ //
+ // Save the OWF encrypted versions of the passwords.
+ //
+
+ Status = RtlCalculateLmOwfPassword( LmPassword,
+ &Credential->LmOwfPassword );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ Credential->LmPasswordPresent = LmPasswordPresent;
+
+ Status = RtlCalculateNtOwfPassword( CleartextPassword,
+ &Credential->NtOwfPassword );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ Credential->NtPasswordPresent = ( CleartextPassword->Length != 0 );
+
+
+ //
+ // Don't leave passwords around in the pagefile
+ //
+
+ RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
+
+
+
+ //
+ // Return the credential to the caller.
+ //
+ *CredentialBuffer = Credential;
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NlpAddPrimaryCredential(
+ IN PLUID LogonId,
+ IN PMSV1_0_PRIMARY_CREDENTIAL Credential,
+ IN ULONG CredentialSize
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine sets a primary credential for the given LogonId.
+
+Arguments:
+
+ LogonId - The LogonId of the LogonSession to set the Credentials
+ for.
+
+ Credential - Specifies a pointer to the credential.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ STRING CredentialString;
+ STRING PrimaryKeyValue;
+
+ //
+ // Make all pointers in the credential relative.
+ //
+
+ NlpMakeRelativeString( (PUCHAR)Credential, &Credential->UserName );
+ NlpMakeRelativeString( (PUCHAR)Credential, &Credential->LogonDomainName );
+
+ //
+ // Add the credential to the logon session.
+ //
+
+ RtlInitString( &PrimaryKeyValue, MSV1_0_PRIMARY_KEY );
+ CredentialString.Buffer = (PCHAR) Credential;
+ CredentialString.Length = (USHORT) CredentialSize;
+ CredentialString.MaximumLength = CredentialString.Length;
+
+ Status = (*Lsa.AddCredential)(
+ LogonId,
+ MspAuthenticationPackageId,
+ &PrimaryKeyValue,
+ &CredentialString );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ KdPrint(( "NlpAddPrimaryCredential: error from AddCredential %lX\n",
+ Status));
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NlpGetPrimaryCredential(
+ IN PLUID LogonId,
+ OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
+ OUT PULONG CredentialSize OPTIONAL
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine gets a primary credential for the given LogonId.
+
+Arguments:
+
+ LogonId - The LogonId of the LogonSession to retrieve the Credentials
+ for.
+
+ CredentialBuffer - Returns a pointer to the specified credential allocated
+ on the LsaHeap. It is the callers responsibility to deallocate
+ this credential.
+
+ CredentialSize - Optionally returns the size of the credential buffer.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG QueryContext = 0;
+ ULONG PrimaryKeyLength;
+ STRING PrimaryKeyValue;
+ STRING CredentialString;
+ PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
+
+ RtlInitString( &PrimaryKeyValue, MSV1_0_PRIMARY_KEY );
+
+ Status = (*Lsa.GetCredentials)( LogonId,
+ MspAuthenticationPackageId,
+ &QueryContext,
+ (BOOLEAN) FALSE, // Just retrieve primary
+ &PrimaryKeyValue,
+ &PrimaryKeyLength,
+ &CredentialString );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // Make all pointers in the credential absolute.
+ //
+
+ Credential = (PMSV1_0_PRIMARY_CREDENTIAL) CredentialString.Buffer;
+
+ NlpRelativeToAbsolute( Credential,
+ (PULONG)&Credential->UserName.Buffer );
+ NlpRelativeToAbsolute( Credential,
+ (PULONG)&Credential->LogonDomainName.Buffer );
+
+
+ *CredentialBuffer = Credential;
+ if ( CredentialSize != NULL ) {
+ *CredentialSize = CredentialString.Length;
+ }
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NlpDeletePrimaryCredential(
+ IN PLUID LogonId
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine deletes the credential for the given LogonId.
+
+Arguments:
+
+ LogonId - The LogonId of the LogonSession to delete the Credentials for.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the logon
+ could not be completed because the client does not have
+ sufficient quota to allocate the return buffer.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ STRING PrimaryKeyValue;
+
+ RtlInitString( &PrimaryKeyValue, MSV1_0_PRIMARY_KEY );
+
+ Status = (*Lsa.DeleteCredential)( LogonId,
+ MspAuthenticationPackageId,
+ &PrimaryKeyValue );
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NlpChangePassword(
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING UserName,
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PNT_OWF_PASSWORD NtOwfPassword
+ )
+
+/*++
+
+Routine Description:
+
+ Change the password for the specified user in all currently stored
+ credentials.
+
+Arguments:
+
+ DomainName - The name of the domain in which the account exists.
+
+ UserName - The name of the account whose password is to be changed.
+
+ LmOwfPassword - The new LM compatible password.
+
+ NtOwfPassword - The new NT compatible password.
+
+Return Value:
+
+ STATUS_SUCCESS - If the operation was successful.
+
+--*/
+{
+ NTSTATUS Status;
+ PACTIVE_LOGON Logon;
+ ULONG LogonCount = 0;
+ PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
+ ULONG CredentialSize;
+
+
+ //
+ // Loop through the table looking for this particular UserName/DomainName.
+ //
+
+ NlpLockActiveLogons();
+
+ for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {
+
+ if(RtlEqualUnicodeString( UserName, &Logon->UserName, (BOOLEAN) TRUE) &&
+ RtlEqualDomainName( DomainName, &Logon->LogonDomainName )){
+
+ //
+ // Get the current credential for this logonid.
+ //
+
+ Status = NlpGetPrimaryCredential( &Logon->LogonId,
+ &Credential,
+ &CredentialSize );
+
+ if ( !NT_SUCCESS(Status) ) {
+ break;
+ }
+
+ //
+ // Delete it from the LSA.
+ //
+
+ Status = NlpDeletePrimaryCredential( &Logon->LogonId );
+
+ if ( !NT_SUCCESS(Status) ) {
+ (*Lsa.FreeLsaHeap)( Credential );
+ break;
+ }
+
+ //
+ // Change the passwords in it
+ //
+
+ Credential->LmOwfPassword = *LmOwfPassword;
+ Credential->NtOwfPassword = *NtOwfPassword;
+
+ //
+ // Add it back to the LSA.
+ //
+
+ Status = NlpAddPrimaryCredential( &Logon->LogonId,
+ Credential,
+ CredentialSize );
+
+ (*Lsa.FreeLsaHeap)( Credential );
+
+ if ( !NT_SUCCESS(Status) ) {
+ break;
+ }
+
+ }
+
+ }
+
+ NlpUnlockActiveLogons();
+
+ //
+ // Pass the new password on to the logon cache
+ //
+
+ NlpChangeCachePassword(
+ DomainName,
+ UserName,
+ LmOwfPassword,
+ NtOwfPassword );
+
+ return Status;
+}
diff --git a/private/lsa/msv1_0/nlp.h b/private/lsa/msv1_0/nlp.h
new file mode 100644
index 000000000..08261a53b
--- /dev/null
+++ b/private/lsa/msv1_0/nlp.h
@@ -0,0 +1,473 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ nlp.h
+
+Abstract:
+
+ NETLOGON private definitions.
+
+
+
+
+Author:
+
+ Jim Kelly 11-Apr-1991
+
+Revision History:
+
+--*/
+
+#ifndef _NLP_
+#define _NLP_
+
+#include <windef.h>
+#include <winbase.h>
+#include <crypt.h>
+#include <lmcons.h>
+#include <ntsam.h>
+#include <ntsamp.h>
+#include <logonmsv.h>
+#include <samrpc.h>
+#include <align.h>
+
+
+//
+// nlmain.c will #include this file with NLP_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef NLP_ALLOCATE
+#define EXTERN
+#define INIT(_X) = _X
+#else
+#define EXTERN extern
+#define INIT(_X)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Private data structures //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Structure used to keep track of all private information related to a
+// particular LogonId.
+//
+
+typedef struct _PACTIVE_LOGON {
+
+ LUID LogonId; // The logon Id of this logon session
+
+ ULONG EnumHandle; // The enumeration handle of this logon session
+
+ SECURITY_LOGON_TYPE LogonType; // Type of logon (interactive or service)
+
+ PSID UserSid; // Sid of the logged on user
+
+ UNICODE_STRING UserName; // Name of the logged on user
+
+ UNICODE_STRING LogonDomainName; // Name of the domain logged onto
+
+ UNICODE_STRING LogonServer; // Name of the server which logged this user on
+
+ ULONG Flags; // Attributes of this entry.
+
+#define LOGON_BY_NETLOGON 0x01 // Entry was validated by NETLOGON service
+#define LOGON_BY_CACHE 0x02 // Entry was validated by local cache
+
+ struct _PACTIVE_LOGON * Next; // Next entry in linked list.
+
+} ACTIVE_LOGON, *PACTIVE_LOGON;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// CREDENTIAL Related Data Structures //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Following is a description of the content and format of each type
+// of credential maintained by the MsV1_0 authentication package.
+//
+// The MsV1_0 authentication package defines the following credential
+// primary key string values:
+//
+// "Primary" - Is used to hold the primary credentials provided at
+// initial logon time. This includes the username and both
+// case-sensitive and case-insensitive forms of the user's
+// password.
+//
+// NOTE: All poitners stored in credentials must be
+// changed to be an offset to the body rather than a pointer. This is
+// because credential fields are copied by the LSA and so the pointer
+// would become invalid.
+//
+
+
+//
+// MsV1_0 Primary Credentials
+//
+//
+// The PrimaryKeyValue string of this type of credential contains the
+// following string:
+//
+// "Primary"
+//
+// The Credential string of a Primary credential contains the following
+// values:
+//
+// o The user's username
+//
+// o A one-way function of the user's password as typed.
+//
+// o A one-way function of the user's password upper-cased.
+//
+// These values are structured as follows:
+//
+
+#define MSV1_0_PRIMARY_KEY "Primary"
+
+typedef struct _MSV1_0_PRIMARY_CREDENTIAL {
+ UNICODE_STRING LogonDomainName;
+ UNICODE_STRING UserName;
+ NT_OWF_PASSWORD NtOwfPassword;
+ LM_OWF_PASSWORD LmOwfPassword;
+ BOOLEAN NtPasswordPresent;
+ BOOLEAN LmPasswordPresent;
+} MSV1_0_PRIMARY_CREDENTIAL, *PMSV1_0_PRIMARY_CREDENTIAL;
+
+
+
+//
+// Structure describing a buffer in the clients address space.
+//
+
+typedef struct _CLIENT_BUFFER_DESC {
+ PLSA_CLIENT_REQUEST ClientRequest;
+ LPBYTE UserBuffer; // Address of buffer in client's address space
+ LPBYTE MsvBuffer; // Address of mirror buffer in MSV's address space
+ ULONG StringOffset; // Current offset to variable length data
+ ULONG TotalSize; // Size (in bytes) of buffer
+} CLIENT_BUFFER_DESC, *PCLIENT_BUFFER_DESC;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Internal routine definitions //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// From nlp.c.
+//
+
+VOID
+NlpPutString(
+ IN PUNICODE_STRING OutString,
+ IN PUNICODE_STRING InString,
+ IN PUCHAR *Where
+ );
+
+VOID
+NlpInitClientBuffer(
+ OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ IN PLSA_CLIENT_REQUEST ClientRequest
+ );
+
+NTSTATUS
+NlpAllocateClientBuffer(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ IN ULONG FixedSize,
+ IN ULONG TotalSize
+ );
+
+NTSTATUS
+NlpFlushClientBuffer(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ OUT PVOID* UserBuffer
+ );
+
+VOID
+NlpFreeClientBuffer(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc
+ );
+
+VOID
+NlpPutClientString(
+ IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
+ IN PUNICODE_STRING OutString,
+ IN PUNICODE_STRING InString
+ );
+
+VOID
+NlpMakeRelativeString(
+ IN PUCHAR BaseAddress,
+ IN OUT PUNICODE_STRING String
+ );
+
+VOID
+NlpRelativeToAbsolute(
+ IN PVOID BaseAddress,
+ IN OUT PULONG RelativeValue
+ );
+
+BOOLEAN
+NlpFindActiveLogon(
+ IN PLUID LogonId,
+ OUT PACTIVE_LOGON **ActiveLogon
+ );
+
+ULONG
+NlpCountActiveLogon(
+ IN PUNICODE_STRING LogonDomainName,
+ IN PUNICODE_STRING UserName
+ );
+
+NTSTATUS
+NlpAllocateInteractiveProfile (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ OUT PMSV1_0_INTERACTIVE_PROFILE *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 NlpUser
+ );
+
+NTSTATUS
+NlpAllocateNetworkProfile (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ OUT PMSV1_0_LM20_LOGON_PROFILE *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 NlpUser,
+ IN ULONG ParameterControl
+ );
+
+PSID
+NlpMakeDomainRelativeSid(
+ IN PSID DomainId,
+ IN ULONG RelativeId
+ );
+
+NTSTATUS
+NlpMakeTokenInformationV1(
+ IN PNETLOGON_VALIDATION_SAM_INFO2 NlpUser,
+ OUT PLSA_TOKEN_INFORMATION_V1 *TokenInformation
+ );
+
+NTSTATUS
+NlpMakePrimaryCredential(
+ IN PUNICODE_STRING LogonDomainName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING CleartextPassword,
+ OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
+ OUT PULONG CredentialSize
+ );
+
+NTSTATUS
+NlpAddPrimaryCredential(
+ IN PLUID LogonId,
+ IN PMSV1_0_PRIMARY_CREDENTIAL Credential,
+ IN ULONG CredentialSize
+ );
+
+NTSTATUS
+NlpGetPrimaryCredential(
+ IN PLUID LogonId,
+ OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
+ OUT PULONG CredentialSize
+ );
+
+NTSTATUS
+NlpDeletePrimaryCredential(
+ IN PLUID LogonId
+ );
+
+NTSTATUS
+NlpChangePassword(
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING UserName,
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PNT_OWF_PASSWORD NtOwfPassword
+ );
+
+
+//
+// msvsam.c
+//
+
+BOOLEAN
+MsvpPasswordValidate (
+ IN BOOLEAN UasCompatibilityRequired,
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN PUSER_INTERNAL1_INFORMATION Passwords,
+ OUT PULONG UserFlags,
+ OUT PUSER_SESSION_KEY UserSessionKey,
+ OUT PLM_SESSION_KEY LmSessionKey
+);
+
+
+
+
+//
+// nlnetapi.c
+//
+
+VOID
+NlpLoadNetapiDll (
+ VOID
+ );
+
+VOID
+NlpLoadNetlogonDll (
+ VOID
+ );
+
+//
+// subauth.c
+//
+
+VOID
+Msv1_0SubAuthenticationInitialization(
+ VOID
+);
+
+
+///////////////////////////////////////////////////////////////////////
+// //
+// Global variables //
+// //
+///////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// //
+// READ ONLY Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+//
+// Null copies of Lanman and NT OWF password.
+//
+//
+
+EXTERN LM_OWF_PASSWORD NlpNullLmOwfPassword;
+EXTERN NT_OWF_PASSWORD NlpNullNtOwfPassword;
+
+//
+// Routines in NetApi32.dll
+//
+
+EXTERN BOOLEAN NlpNetapiDllLoaded;
+EXTERN NET_API_STATUS (NET_API_FUNCTION *NlpNetGetDCName)(LPWSTR, LPWSTR, LPBYTE *);
+EXTERN NET_API_STATUS (NET_API_FUNCTION *NlpNetApiBufferFree)(LPVOID);
+EXTERN NET_API_STATUS (NET_API_FUNCTION *NlpRxNetUserPasswordSet)(LPWSTR, LPWSTR, LPWSTR, LPWSTR);
+EXTERN NTSTATUS (*NlpNetpApiStatusToNtStatus)( NET_API_STATUS );
+
+//
+// Routines in netlogon.dll
+//
+
+EXTERN HANDLE NlpNetlogonDllHandle;
+EXTERN PNETLOGON_SAM_LOGON_PROCEDURE NlpNetLogonSamLogon;
+EXTERN PNETLOGON_SAM_LOGOFF_PROCEDURE NlpNetLogonSamLogoff;
+
+//
+// TRUE if this is a workstation.
+//
+
+EXTERN BOOLEAN NlpWorkstation INIT(TRUE);
+
+//
+// TRUE once the MSV AP has initialized its connection to SAM.
+//
+
+EXTERN BOOLEAN NlpSamInitialized INIT(FALSE);
+
+//
+// TRUE if the MSV AP has initialized its connection to the NETLOGON service
+//
+
+EXTERN BOOLEAN NlpNetlogonInitialized INIT(FALSE);
+
+//
+// TRUE if LanMan is installed.
+//
+
+EXTERN BOOLEAN NlpLanmanInstalled INIT(FALSE);
+
+//
+// Computername of this computer.
+//
+
+EXTERN UNICODE_STRING NlpComputerName;
+
+//
+// Name and domain id of the SAM account database.
+//
+
+EXTERN UNICODE_STRING NlpSamDomainName;
+EXTERN PSID NlpSamDomainId;
+EXTERN SAMPR_HANDLE NlpSamDomainHandle;
+EXTERN BOOLEAN NlpUasCompatibilityRequired INIT(TRUE);
+
+//
+// Trusted Handle to the Lsa database.
+//
+
+EXTERN LSA_HANDLE NlpPolicyHandle INIT(NULL);
+
+//
+// TRUE if there is a subauthentication package zero
+//
+
+EXTERN BOOLEAN NlpSubAuthZeroExists INIT(TRUE);
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// READ/WRITE Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+//
+// Define the list of active interactive logons.
+//
+// The NlpActiveLogonLock must be locked while referencing the list or
+// any of its elements.
+//
+
+#define NlpLockActiveLogons() RtlEnterCriticalSection(&NlpActiveLogonLock)
+#define NlpUnlockActiveLogons() RtlLeaveCriticalSection(&NlpActiveLogonLock)
+
+EXTERN RTL_CRITICAL_SECTION NlpActiveLogonLock;
+EXTERN PACTIVE_LOGON NlpActiveLogons;
+
+//
+// Define the running enumeration handle.
+//
+// This variable defines the enumeration handle to assign to a logon
+// session. It will be incremented prior to assigning it value to
+// the next created logon session. Access is serialize using
+// NlpActiveLogonLocks.
+
+EXTERN ULONG NlpEnumerationHandle;
+
+//
+// Define a running Session Number which is incremented once for each
+// challenge given to the server.
+//
+
+EXTERN RTL_CRITICAL_SECTION NlpSessionCountLock;
+EXTERN ULONG NlpSessionCount;
+EXTERN ULONG NlpLogonAttemptCount;
+
+
+#undef EXTERN
+#undef INIT
+#endif _NLP_
diff --git a/private/lsa/msv1_0/nlpcache.c b/private/lsa/msv1_0/nlpcache.c
new file mode 100644
index 000000000..72b7a44b3
--- /dev/null
+++ b/private/lsa/msv1_0/nlpcache.c
@@ -0,0 +1,4561 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nlpcache.c
+
+Abstract:
+
+ This module contains routines which implement user account caching:
+
+ NlpCacheInitialize
+ NlpCacheTerminate
+ NlpAddCacheEntry
+ NlpGetCacheEntry
+ NlpDeleteCacheEntry
+ NlpChangeCachePassword
+
+
+ The cache contains the most recent validated logon information. There is
+ only 1 (that's right - one) cache slot. This will probably change though
+
+Author:
+
+ Richard L Firth (rfirth) 17-Dec-1991
+
+ BUGBUG - To Be Done:
+
+ 1. call Lsar routines instead of going via Rpc. Use:
+ LsaIOpenPolicyTruseted()
+ Lsar routines to do work
+ LsaIFree() to free returned buffers
+
+ 2. Security - stop anybody viewing cache in registry
+
+Revision History:
+
+ 17-Dec-1991 rfirth
+ Created
+
+--*/
+
+#include "msp.h"
+#include "nlp.h"
+#include "nlpcache.h"
+#include <ntrpcp.h> // MIDL_user_{allocate!free}
+
+//
+// manifests
+//
+
+#if DBG
+#include <stdio.h>
+#endif
+
+//
+// Revision numbers
+//
+// The first release of NT (NT1.0) didn't explicitly store
+// a revision number. However, we are designating that release
+// to be revision 0x00010000 (1.0). NT1.0A is being designated
+// as revision 0x00010002 (1.2). Previous to build 622 is revision
+// 0x00010001 (1.1).
+//
+
+#define NLP_CACHE_REVISION_NT_1_0 (0x00010000)
+#define NLP_CACHE_REVISION_NT_1_0A (0x00010001)
+#define NLP_CACHE_REVISION_NT_1_0B (0x00010002)
+#define NLP_CACHE_REVISION (NLP_CACHE_REVISION_NT_1_0B)
+
+
+
+
+
+//
+// The logon cache may be controlled via a value in the registry.
+// If the registry key does not exist, then this default constant defines
+// how many logon cache entries will be active. The max constant
+// places an upper limit on how many cache entries we will support.
+// If the user specifies more than the max value, we will use the
+// max value instead.
+//
+
+#define NLP_DEFAULT_LOGON_CACHE_COUNT (10)
+#define NLP_MAX_LOGON_CACHE_COUNT (50)
+
+
+
+//
+// macros
+//
+
+#define AllocateCacheEntry(n) (PLOGON_CACHE_ENTRY)RtlAllocateHeap(MspHeap, 0, n)
+#define FreeCacheEntry(p) RtlFreeHeap(MspHeap, 0, (PVOID)p)
+#define AllocateFromHeap(n) RtlAllocateHeap(MspHeap, 0, n)
+#define FreeToHeap(p) RtlFreeHeap(MspHeap, 0, (PVOID)p)
+
+//
+// guard against simultaneous access
+//
+
+#define ENTER_CACHE() RtlEnterCriticalSection(&NlpLogonCacheCritSec)
+#define LEAVE_CACHE() RtlLeaveCriticalSection(&NlpLogonCacheCritSec)
+
+#define INVALIDATE_HANDLE(handle) (*((PHANDLE)(&handle)) = INVALID_HANDLE_VALUE)
+#define IS_VALID_HANDLE(handle) (handle != INVALID_HANDLE_VALUE)
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// datatypes //
+// //
+////////////////////////////////////////////////////////////////////////
+
+typedef enum _NLP_SET_TIME_HINT {
+ NLP_SMALL_TIME,
+ NLP_BIG_TIME,
+ NLP_NOW_TIME
+} NLP_SET_TIME_HINT, *PNLP_SET_TIME_HINT;
+
+#define BIG_PART_1 0x7fffffff // largest positive large int is 63 bits on
+#define BIG_PART_2 0xffffffff
+#define SMALL_PART_1 0x0 // smallest positive large int is 64 bits off
+#define SMALL_PART_2 0x0
+
+
+//
+// This structure is saved on disk and provides information
+// about the rest of the cache. This structure is in a value
+// named "NL$Control" under the cache registry key.
+//
+
+typedef struct _NLP_CACHE_CONTROL {
+
+ //
+ // Revision of the cache on-disk structure
+ //
+
+ ULONG Revision;
+
+ //
+ // The current on-disk size of the cache (number of entries)
+ //
+
+ ULONG Entries;
+
+} NLP_CACHE_CONTROL, *PNLP_CACHE_CONTROL;
+
+
+//
+// This data structure is a single cache table entry (CTE)
+// Each entry in the cache has a corresponding CTE.
+//
+
+typedef struct _NLP_CTE {
+
+ //
+ // CTEs are linked on either an invalid list (in any order)
+ // or on a valid list (in ascending order of time).
+ // This makes it easy to figure out which entry is to be
+ // flushed when adding to the cache.
+ //
+
+ LIST_ENTRY Link;
+
+
+ //
+ // Time the cache entry was established.
+ // This is used to determine which cache
+ // entry is the oldest, and therefore will
+ // be flushed from the cache first to make
+ // room for new entries.
+ //
+
+ LARGE_INTEGER Time;
+
+
+ //
+ // This field contains the index of the CTE within the
+ // CTE table. This index is used to generate the names
+ // of the entrie's secret key and cache key in the registry.
+ // This field is valid even if the entry is marked Inactive.
+ //
+
+ ULONG Index;
+
+ //
+ // Normally, we walk the active and inactive lists
+ // to find entries. When growing or shrinking the
+ // cache, however, it is nice to be able to walk the
+ // table using indexes. In this case, it is nice to
+ // have a local way of determining whether an entry
+ // is on the active or inactive list. This field
+ // provides that capability.
+ //
+ // TRUE ==> on active list
+ // FALSE ==> not on active list
+ //
+
+ BOOLEAN Active;
+
+
+} NLP_CTE, *PNLP_CTE;
+
+//
+// This structure is used for keeping track of all information that
+// is stored on backing store.
+//
+
+typedef struct _NLP_CACHE_AND_SECRETS {
+ PLOGON_CACHE_ENTRY CacheEntry;
+ ULONG EntrySize;
+ PUNICODE_STRING NewSecret;
+ PUNICODE_STRING OldSecret;
+ BOOLEAN Active;
+} NLP_CACHE_AND_SECRETS, *PNLP_CACHE_AND_SECRETS;
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Local Prototypes //
+// //
+////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+NlpInternalCacheInitialize(
+ VOID
+ );
+
+NTSTATUS
+NlpOpenCache( VOID );
+
+VOID
+NlpCloseCache( VOID );
+
+
+NTSTATUS
+NlpGetCacheControlInfo( VOID );
+
+
+NTSTATUS
+NlpBuildCteTable( VOID );
+
+NTSTATUS
+NlpChangeCacheSizeIfNecessary( VOID );
+
+NTSTATUS
+NlpWriteCacheControl( VOID );
+
+VOID
+NlpMakeCacheEntryName(
+ IN ULONG EntryIndex,
+ OUT PUNICODE_STRING Name
+ );
+
+NTSTATUS
+NlpMakeNewCacheEntry(
+ ULONG Index
+ );
+
+NTSTATUS
+NlpEliminateCacheEntry(
+ IN ULONG Index
+ );
+
+NTSTATUS
+NlpConvert1_0To1_0B( VOID );
+
+NTSTATUS
+NlpOpen_Nt1_0_Secret( VOID );
+
+NTSTATUS
+NlpReadCacheEntryByIndex(
+ IN ULONG Index,
+ OUT PLOGON_CACHE_ENTRY* CacheEntry,
+ OUT PULONG EntrySize
+ );
+
+VOID
+NlpAddEntryToActiveList(
+ IN ULONG Index
+ );
+
+VOID
+NlpAddEntryToInactiveList(
+ IN ULONG Index
+ );
+
+VOID
+NlpGetFreeEntryIndex(
+ OUT PULONG Index
+ );
+
+
+NTSTATUS
+NlpBuildCacheEntry(
+ IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 AccountInfo,
+ OUT PLOGON_CACHE_ENTRY* ppCacheEntry,
+ OUT PULONG pEntryLength
+ );
+
+NTSTATUS
+NlpOpenCache( VOID );
+
+VOID
+NlpCloseCache( VOID );
+
+NTSTATUS
+NlpOpenSecret(
+ IN ULONG Index
+ );
+
+VOID
+NlpCloseSecret( VOID );
+
+NTSTATUS
+NlpWriteSecret(
+ IN PUNICODE_STRING NewSecret,
+ IN PUNICODE_STRING OldSecret
+ );
+
+NTSTATUS
+NlpReadSecret(
+ OUT PUNICODE_STRING* NewSecret,
+ OUT PUNICODE_STRING* OldSecret
+ );
+
+NTSTATUS
+NlpMakeSecretPassword(
+ OUT PUNICODE_STRING Passwords,
+ IN PNT_OWF_PASSWORD NtOwfPassword OPTIONAL,
+ IN PLM_OWF_PASSWORD LmOwfPassword OPTIONAL
+ );
+
+NTSTATUS
+NlpReadCacheEntry(
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING UserName,
+ OUT PULONG Index,
+ OUT PLOGON_CACHE_ENTRY* CacheEntry,
+ OUT PULONG EntrySize
+ );
+
+NTSTATUS
+NlpWriteCacheEntry(
+ IN ULONG Index,
+ IN PLOGON_CACHE_ENTRY Entry,
+ IN ULONG EntrySize
+ );
+
+VOID
+NlpCopyAndUpdateAccountInfo(
+ IN USHORT Length,
+ IN PUNICODE_STRING pUnicodeString,
+ IN OUT PUCHAR* pSource,
+ IN OUT PUCHAR* pDest
+ );
+
+VOID
+NlpSetTimeField(
+ OUT POLD_LARGE_INTEGER pTimeField,
+ IN NLP_SET_TIME_HINT Hint
+ );
+
+NTSTATUS
+NlpBuildAccountInfo(
+ IN PLOGON_CACHE_ENTRY pCacheEntry,
+ IN ULONG EntryLength,
+ OUT PNETLOGON_VALIDATION_SAM_INFO2* AccountInfo
+ );
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Diagnostic support services prototypes //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+#if DBG
+PCHAR
+DumpOwfPasswordToString(
+ OUT PCHAR Buffer,
+ IN PLM_OWF_PASSWORD Password
+ );
+
+VOID
+DumpLogonInfo(
+ IN PNETLOGON_LOGON_IDENTITY_INFO LogonInfo
+ );
+
+char*
+MapWeekday(
+ IN CSHORT Weekday
+ );
+
+VOID
+DumpTime(
+ IN LPSTR String,
+ IN POLD_LARGE_INTEGER OldTime
+ );
+
+VOID
+DumpGroupIds(
+ IN LPSTR String,
+ IN ULONG Count,
+ IN PGROUP_MEMBERSHIP GroupIds
+ );
+
+VOID
+DumpSessKey(
+ IN LPSTR String,
+ IN PUSER_SESSION_KEY Key
+ );
+
+VOID
+DumpSid(
+ LPSTR String,
+ PISID Sid
+ );
+
+VOID
+DumpAccountInfo(
+ IN PNETLOGON_VALIDATION_SAM_INFO2 AccountInfo
+ );
+
+VOID
+DumpCacheEntry(
+ IN ULONG Index,
+ IN PLOGON_CACHE_ENTRY pEntry
+ );
+
+#endif //DBG
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// global data //
+// //
+////////////////////////////////////////////////////////////////////////
+
+//
+// This boolean indicates whether or not we have been able to
+// initialize caching yet. It turn out that during authentication
+// package load time, we can't do everything we would like to (like
+// call LSA RPC routines). So, we delay initializing until we can
+// call LSA. All publicly exposed interfaces must check this value
+// before assuming work can be done.
+//
+
+BOOLEAN NlpInitializationNotYetPerformed = TRUE;
+
+
+RTL_CRITICAL_SECTION NlpLogonCacheCritSec;
+
+
+
+HANDLE NlpCacheHandle = (HANDLE) INVALID_HANDLE_VALUE;
+LSA_HANDLE NlpLsaHandle = (LSA_HANDLE) INVALID_HANDLE_VALUE;
+LSA_HANDLE NlpSecretHandle = (LSA_HANDLE) INVALID_HANDLE_VALUE;
+
+//
+// control information about the cache (number of entries, etc).
+//
+
+NLP_CACHE_CONTROL NlpCacheControl;
+
+//
+// This structure is generated and maintained only in memory.
+// It indicates which cache entries are valid and which aren't.
+// It also indicates what time each entry was established so we
+// know which order to discard them in.
+//
+// This field is a pointer to an array of CTEs. The number of CTEs
+// in the array is in NlpCacheControl.Entries. This structure is
+// allocated at initialization time.
+//
+
+PNLP_CTE NlpCteTable;
+
+
+//
+// The Cache Table Entries in NlpCteTable are linked on either an
+// active or inactive list. The entries on the active list are in
+// ascending time order - so the last one on the list is the first
+// one to be discarded when a flush is needed to add a new entry.
+//
+
+LIST_ENTRY NlpActiveCtes;
+LIST_ENTRY NlpInactiveCtes;
+
+
+
+
+#if DBG
+#ifdef DUMP_CACHE_INFO
+ULONG DumpCacheInfo = 1;
+#else
+ULONG DumpCacheInfo = 0;
+#endif
+#endif
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Services Exported by this module //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+NTSTATUS
+NlpCacheInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to initialize cached logon processing.
+
+ Unfortunately, there isn't much we can do when we are called.
+ (we can't open LSA, for example). So, defer initialization
+ until later.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+ return RtlInitializeCriticalSection(&NlpLogonCacheCritSec);
+}
+
+
+NTSTATUS
+NlpCacheTerminate(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Called when process detaches
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpCacheTerminate\n");
+ }
+#endif
+
+ if (!NlpInitializationNotYetPerformed) {
+ NlpCloseCache();
+ NlpCloseSecret();
+
+ if (IS_VALID_HANDLE(NlpLsaHandle)) {
+ LsaClose( NlpLsaHandle );
+ }
+ if (IS_VALID_HANDLE(NlpCacheHandle)) {
+ NtClose( NlpCacheHandle );
+ }
+
+ FreeToHeap( NlpCteTable );
+ }
+
+ return RtlDeleteCriticalSection(&NlpLogonCacheCritSec);
+
+}
+
+
+NTSTATUS
+NlpGetCacheEntry(
+ IN PNETLOGON_LOGON_IDENTITY_INFO LogonInfo,
+ OUT PNETLOGON_VALIDATION_SAM_INFO2* AccountInfo,
+ OUT PCACHE_PASSWORDS Passwords
+ )
+
+/*++
+
+Routine Description:
+
+ If the user logging on has information stored in the cache,
+ then it is retrieved. Also returns the cached password from
+ 'secret' storage
+
+Arguments:
+
+ LogonInfo - pointer to NETLOGON_IDENTITY_INFO structure which contains
+ the domain name, user name for this user
+
+ AccountInfo - pointer to NETLOGON_VALIDATION_SAM_INFO2 structure to
+ receive this user's specific interactive logon information
+
+ Passwords - pointer to CACHE_PASSWORDS structure to receive passwords
+ returned from secret storage
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ *AccountInfo points to a NETLOGON_VALIDATION_SAM_INFO2
+ structure. This must be freed by caller
+
+ *Passwords contain USER_INTERNAL1_INFORMATION structure
+ which contains NT OWF password and LM OWF password. These
+ must be used to validate the logon
+
+ Failure = STATUS_LOGON_FAILURE
+ The user logging on isn't in the cache.
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ PNETLOGON_VALIDATION_SAM_INFO2
+ SamInfo;
+
+ PLOGON_CACHE_ENTRY
+ CacheEntry;
+
+ ULONG
+ EntrySize,
+ Index;
+
+ PUNICODE_STRING
+ CurrentSecret = NULL,
+ OldSecret = NULL;
+
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpGetCacheEntry\n");
+ DumpLogonInfo(LogonInfo);
+ }
+#endif
+
+ if (NlpInitializationNotYetPerformed) {
+ NtStatus = NlpInternalCacheInitialize();
+ if (!NT_SUCCESS(NtStatus)) {
+ return(NtStatus);
+ }
+ }
+
+ if (NlpCacheControl.Entries == 0) {
+ return(STATUS_LOGON_FAILURE);
+ }
+
+ ENTER_CACHE();
+
+
+
+ //
+ // Find the cache entry and open its secret (if found)
+ //
+
+ NtStatus = NlpReadCacheEntry(&LogonInfo->LogonDomainName,
+ &LogonInfo->UserName,
+ &Index,
+ &CacheEntry,
+ &EntrySize);
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpBuildAccountInfo(CacheEntry, EntrySize, &SamInfo);
+ if (NT_SUCCESS(NtStatus)) {
+
+
+ NtStatus = NlpReadSecret(&CurrentSecret, &OldSecret);
+ if (NT_SUCCESS(NtStatus)) {
+
+ if (CurrentSecret) {
+ RtlCopyMemory((PVOID)Passwords,
+ (PVOID)CurrentSecret->Buffer,
+ (ULONG)CurrentSecret->Length
+ );
+ }
+ }
+ }
+
+ //
+ // free structure allocated by NlpReadCacheEntry
+ //
+
+ FreeToHeap(CacheEntry);
+ }
+
+ //
+ // free structures allocated by NlpReadSecret
+ //
+
+ if (CurrentSecret) {
+ MIDL_user_free(CurrentSecret);
+ }
+ if (OldSecret) {
+ MIDL_user_free(OldSecret);
+ }
+
+
+ LEAVE_CACHE();
+
+ *AccountInfo = SamInfo; // undefined if error
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpAddCacheEntry(
+ IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 AccountInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Adds this domain:user interactive logon information to the cache.
+
+Arguments:
+
+ LogonInfo - pointer to NETLOGON_INTERACTIVE_INFO structure which contains
+ the domain name, user name and password for this user. These
+ are what the user typed to WinLogon
+
+ AccountInfo - pointer to NETLOGON_VALIDATION_SAM_INFO2 structure which
+ contains this user's specific interactive logon information
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ AccountInfo successfully added to cache
+
+ Failure = STATUS_NO_MEMORY
+
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ PLOGON_CACHE_ENTRY
+ CacheEntry;
+
+ ULONG
+ EntrySize,
+ Index;
+
+ PUNICODE_STRING
+ CurrentSecret = NULL,
+ OldSecret = NULL;
+
+ UNICODE_STRING
+ Passwords;
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpAddCacheEntry\n");
+ DumpLogonInfo(&LogonInfo->Identity);
+ DumpAccountInfo(AccountInfo);
+ }
+#endif
+
+ if (NlpInitializationNotYetPerformed) {
+ NtStatus = NlpInternalCacheInitialize();
+ if (!NT_SUCCESS(NtStatus)) {
+ return(NtStatus);
+ }
+ }
+
+ if (NlpCacheControl.Entries == 0) {
+ return(STATUS_SUCCESS);
+ }
+ ENTER_CACHE();
+
+ //
+ // See if this entry already exists in the cache.
+ // If so, use the same index.
+ //
+
+ NtStatus = NlpReadCacheEntry(&LogonInfo->Identity.LogonDomainName,
+ &LogonInfo->Identity.UserName,
+ &Index,
+ &CacheEntry,
+ &EntrySize
+ );
+
+ //
+ // If we didn't find an entry, then we need to allocate an
+ // entry.
+ //
+
+ if (!NT_SUCCESS(NtStatus)) {
+
+ NlpGetFreeEntryIndex( &Index );
+
+ } else {
+
+ //
+ // We already have an entry for this user.
+ // Discard the structure we got back but
+ // use the same index.
+ //
+
+ FreeToHeap( CacheEntry );
+ }
+
+
+ //
+ // we have to store the information in two parts - the NETLOGON_VALIDATION_SAM_INFO2
+ // (or part thereof) goes in the registry in the \Machine\Security\Cache
+ // leaf. We also need to store the password the user used to logon. This
+ // goes in the LSA 'secret' storage. We have the familiar integrity problem
+ // of having to store 2 bits of info in 2 separate locations.
+ //
+ // Since the secret storage stores a new and an old value together, we will
+ // use this to remember the previous password. If we update the password in
+ // secret storage, but fail to add the cache entry to the registry, then we
+ // back out the password to the previous (old) value
+ //
+
+ NtStatus = NlpBuildCacheEntry(LogonInfo, AccountInfo, &CacheEntry, &EntrySize);
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpOpenSecret( Index );
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpReadSecret(&CurrentSecret, &OldSecret);
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // LsaSetSecret takes 2 UNICODE_STRING arguments. Convert the
+ // encrypted NT One Way Function (OWF) and LM OWF passwords to
+ // a single UNICODE_STRING
+ //
+
+ NtStatus = NlpMakeSecretPassword(&Passwords,
+ &LogonInfo->NtOwfPassword,
+ &LogonInfo->LmOwfPassword
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpWriteSecret(&Passwords, CurrentSecret);
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpWriteCacheEntry(Index, CacheEntry, EntrySize);
+ if (!NT_SUCCESS(NtStatus)) {
+
+ //
+ // Try to restore old secrets
+ //
+
+ NtStatus = NlpWriteSecret(CurrentSecret, OldSecret);
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+ }
+
+ //
+ // free the buffer allocated to store the passwords
+ //
+
+ FreeToHeap(Passwords.Buffer);
+ }
+
+ //
+ // free secret strings returned from NlpReadSecret
+ //
+
+ if (CurrentSecret) {
+ MIDL_user_free(CurrentSecret);
+ }
+ if (OldSecret) {
+ MIDL_user_free(OldSecret);
+ }
+ }
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+ NlpCteTable[Index].Time = CacheEntry->Time;
+ NlpAddEntryToActiveList( Index );
+ }
+
+ FreeCacheEntry(CacheEntry);
+ }
+
+
+ LEAVE_CACHE();
+
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpDeleteCacheEntry(
+ IN PNETLOGON_INTERACTIVE_INFO LogonInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes a user account from the local user account cache, if the corresponding
+ entry can be found. We actually just null out the current contents instead of
+ destroying the storage - this should save us some time when we next come to
+ add an entry to the cache
+
+Arguments:
+
+ LogonInfo - pointer to NETLOGON_INTERACTIVE_INFO structure which contains
+ the domain name, user name and password for this user
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+
+ Failure =
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ PLOGON_CACHE_ENTRY
+ CacheEntry;
+
+ ULONG
+ EntrySize,
+ Index;
+
+
+ if (NlpInitializationNotYetPerformed) {
+ NtStatus = NlpInternalCacheInitialize();
+ if (!NT_SUCCESS(NtStatus)) {
+ return(NtStatus);
+ }
+ }
+
+
+ if (NlpCacheControl.Entries == 0) {
+ return(STATUS_SUCCESS);
+ }
+ ENTER_CACHE();
+
+ //
+ // See if this entry exists in the cache.
+ //
+
+ NtStatus = NlpReadCacheEntry( &LogonInfo->Identity.LogonDomainName,
+ &LogonInfo->Identity.UserName,
+ &Index,
+ &CacheEntry,
+ &EntrySize
+ );
+
+ //
+ // If we didn't find an entry, then there is nothing to do.
+ //
+
+ if (!NT_SUCCESS(NtStatus)) {
+ LEAVE_CACHE();
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // Mark it as invalid.
+ //
+
+ CacheEntry->Valid = FALSE;
+
+ NtStatus = NlpWriteCacheEntry( Index, CacheEntry, EntrySize );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Put the CTE entry on the inactive list.
+ //
+
+ NlpAddEntryToInactiveList( Index );
+ }
+
+ //
+ // Free the structure returned from NlpReadCacheEntry()
+ //
+
+ FreeToHeap( CacheEntry );
+
+
+
+ LEAVE_CACHE();
+
+ return(NtStatus);
+}
+
+
+VOID
+NlpChangeCachePassword(
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING UserName,
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PNT_OWF_PASSWORD NtOwfPassword
+ )
+
+/*++
+
+Routine Description:
+
+ Update a cached password to the specified value, if we have
+ the specified account cached.
+
+Arguments:
+
+
+ DomainName - The name of the domain in which the account exists.
+
+ UserName - The name of the account whose password is to be changed.
+
+ LmOwfPassword - The new LM compatible password.
+
+ NtOwfPassword - The new NT compatible password.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ PLOGON_CACHE_ENTRY
+ CacheEntry;
+
+ ULONG
+ EntrySize,
+ Index;
+
+ PUNICODE_STRING
+ CurrentSecret = NULL,
+ OldSecret = NULL;
+
+ UNICODE_STRING
+ Passwords;
+
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpChangeCachePassword\n");
+ }
+#endif
+
+ if (NlpInitializationNotYetPerformed) {
+ NtStatus = NlpInternalCacheInitialize();
+ if (!NT_SUCCESS(NtStatus)) {
+ return;
+ }
+ }
+
+
+ if (NlpCacheControl.Entries == 0) {
+ return;
+ }
+
+ ENTER_CACHE();
+
+
+ NtStatus = NlpReadCacheEntry( DomainName,
+ UserName,
+ &Index,
+ &CacheEntry,
+ &EntrySize);
+ if (NT_SUCCESS(NtStatus)) {
+
+
+ NtStatus = NlpOpenSecret( Index );
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpReadSecret(&CurrentSecret, &OldSecret);
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpMakeSecretPassword( &Passwords,
+ NtOwfPassword,
+ LmOwfPassword );
+
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpWriteSecret(&Passwords, CurrentSecret);
+
+ //
+ // free the buffer allocated to store the passwords
+ //
+
+ FreeToHeap(Passwords.Buffer);
+ }
+
+ //
+ // free strings returned by NlpReadSecret
+ //
+
+ if (CurrentSecret) {
+ MIDL_user_free(CurrentSecret);
+ }
+ if (OldSecret) {
+ MIDL_user_free(OldSecret);
+ }
+
+ }
+ }
+
+ //
+ // free structure allocated by NlpReadCacheEntry
+ //
+
+ FreeToHeap(CacheEntry);
+ }
+
+ LEAVE_CACHE();
+
+ return;
+
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Services Internal to this module //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+NlpInternalCacheInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to initialize cached logon processing.
+
+ This routine will automatically adjust the size of the logon
+ cache if necessary to accomodate a new user-specified length
+ (specified in the Winlogon part of the registry).
+
+ NOTE: If called too early, this routine won't be able to call
+ LSA's RPC routines. In this case, initialization is
+ defered until later.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ NTSTATUS
+ NtStatus;
+
+ OBJECT_ATTRIBUTES
+ ObjectAttributes;
+
+//DbgPrint("\n\n\n REMEMBER TO TAKE THIS BREAKPOINT OUT BEFORE CHECKIN.\n\n\n");
+//DumpCacheInfo = 1; // Remember to take this out too !!!!!!
+//DbgBreakPoint(); // Remember to take this out before checking
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpCacheInitialize\n");
+ }
+#endif
+
+
+ //
+ // Upon return from this routine, if logon caching is enabled,
+ // the following will be true:
+ //
+ // A handle to the LsaPolicy object will be open (NlpLsaHandle).
+ //
+ // A handle to the registry key in which all cache entries
+ // are held will be open (NlpCacheHandle).
+ //
+ // A global structure defining how many cache entries there are
+ // will be initialized (NlpCacheControl).
+ //
+ // The Cache Table Entry table (CTE table) will be initialized
+ // (NlpCteTable).
+ //
+ // The active and inactive CTE lists will be built
+ // (NlpActiveCtes and NlpInactiveCtes).
+ //
+
+
+
+
+
+ ENTER_CACHE();
+
+ //
+ // Check again if the cache is initialized now that the crit sect is locked.
+ //
+
+ if (NlpInitializationNotYetPerformed) {
+
+ //
+ // Open the local system's policy object
+ //
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ NULL, // name
+ 0,
+ 0,
+ NULL
+ );
+ NtStatus = LsaOpenPolicy(NULL, // local LSA
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION | POLICY_CREATE_SECRET,
+ &NlpLsaHandle
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Successfully, or unsucessfully,
+ // The definition of "initialized" is we could call LSA's RPC
+ // routines.
+ //
+
+ NlpInitializationNotYetPerformed = FALSE;
+
+ //
+ // Open the registry key containing cache entries.
+ // This will remain open.
+ //
+
+ NtStatus = NlpOpenCache();
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Get information on the current cache structure
+ // (number of entries, et cetera). This information is
+ // placed in a global variable for use throughout this
+ // module.
+ //
+
+ NtStatus = NlpGetCacheControlInfo();
+
+
+ //
+ // Now build the CTE table
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpBuildCteTable();
+ }
+
+ //
+ // If we were successful, then see if we need to change
+ // the cache due to new user-specified cache size.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpChangeCacheSizeIfNecessary();
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ NlpCloseCache();
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ LsaClose( NlpLsaHandle );
+ }
+ }
+
+ //
+ // If we had an error, then set our entry count to zero
+ // to prevent using any cache information.
+ //
+
+ if (!NT_SUCCESS(NtStatus)) {
+ NlpCacheControl.Entries = 0;
+ }
+
+ } else {
+ NtStatus = STATUS_SUCCESS;
+ }
+
+ LEAVE_CACHE();
+
+ return(NtStatus);
+}
+
+
+
+NTSTATUS
+NlpBuildCacheEntry(
+ IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 AccountInfo,
+ OUT PLOGON_CACHE_ENTRY* ppCacheEntry,
+ OUT PULONG pEntryLength
+ )
+
+/*++
+
+Routine Description:
+
+ Builds a LOGON_CACHE_ENTRY from a NETLOGON_VALIDATION_SAM_INFO2 structure.
+ We only cache those fields that we cannot easily re-invent
+
+Arguments:
+
+ LogonInfo - pointer to NETLOGON_INTERACTIVE_INFO structure containing
+ user's name and logon domain name
+
+ AccountInfo - pointer to NETLOGON_VALIDATION_SAM_INFO2 from successful
+ logon
+
+ ppCacheEntry - pointer to place to return pointer to allocated
+ LOGON_CACHE_ENTRY
+
+ pEntryLength - size of the buffer returned in *ppCacheEntry
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ *ppCacheEntry contains pointer to allocated LOGON_CACHE_ENTRY
+ structure
+
+ Failure = STATUS_NO_MEMORY
+ *ppCacheEntry undefined
+
+--*/
+
+{
+ PLOGON_CACHE_ENTRY pEntry;
+ ULONG length;
+ PCHAR dataptr;
+
+#if DBG
+ NTSTATUS NtStatus;
+#endif
+
+ //
+ // assumes GROUP_MEMBERSHIP is integral multiple of DWORDs
+ //
+
+ length = ROUND_UP_COUNT(sizeof(LOGON_CACHE_ENTRY), sizeof(ULONG))
+ + ROUND_UP_COUNT(LogonInfo->Identity.LogonDomainName.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(LogonInfo->Identity.UserName.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->EffectiveName.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->FullName.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->LogonScript.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->ProfilePath.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->HomeDirectory.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->HomeDirectoryDrive.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->LogonDomainName.Length, sizeof(ULONG))
+ + ROUND_UP_COUNT(AccountInfo->GroupCount * sizeof(GROUP_MEMBERSHIP), sizeof(ULONG))
+ + ROUND_UP_COUNT(RtlLengthSid(AccountInfo->LogonDomainId), sizeof(ULONG));
+
+ if (AccountInfo->UserFlags & LOGON_EXTRA_SIDS) {
+ if (AccountInfo->SidCount) {
+ ULONG i;
+ length += ROUND_UP_COUNT(AccountInfo->SidCount * sizeof(ULONG), sizeof(ULONG));
+ for (i = 0; i < AccountInfo->SidCount ; i++ ) {
+ length += ROUND_UP_COUNT(RtlLengthSid(AccountInfo->ExtraSids[i].Sid), sizeof(ULONG));
+ }
+ }
+ }
+
+
+ pEntry = AllocateCacheEntry(length);
+ if (pEntry == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlZeroMemory( pEntry, length );
+ pEntry->Revision = NLP_CACHE_REVISION;
+ NtQuerySystemTime( &pEntry->Time );
+ pEntry->Valid = TRUE;
+ pEntry->Unused1 = 0;
+ pEntry->Unused2 = 0;
+
+
+ dataptr = (PCHAR)(pEntry + 1);
+ *pEntryLength = length;
+
+ ASSERT(!((ULONG)dataptr & (sizeof(ULONG) - 1)));
+
+ //
+ // each of these (unicode) strings and other structures are copied to the
+ // end of the fixed LOGON_CACHE_ENTRY structure, each aligned on DWORD
+ // boundaries
+ //
+
+ length = pEntry->UserNameLength = LogonInfo->Identity.UserName.Length;
+ RtlCopyMemory(dataptr, LogonInfo->Identity.UserName.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->DomainNameLength = LogonInfo->Identity.LogonDomainName.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, LogonInfo->Identity.LogonDomainName.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ length = pEntry->EffectiveNameLength = AccountInfo->EffectiveName.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->EffectiveName.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ length = pEntry->FullNameLength = AccountInfo->FullName.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->FullName.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ length = pEntry->LogonScriptLength = AccountInfo->LogonScript.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->LogonScript.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ length = pEntry->ProfilePathLength = AccountInfo->ProfilePath.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->ProfilePath.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ length = pEntry->HomeDirectoryLength = AccountInfo->HomeDirectory.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->HomeDirectory.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ length = pEntry->HomeDirectoryDriveLength = AccountInfo->HomeDirectoryDrive.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->HomeDirectoryDrive.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ pEntry->UserId = AccountInfo->UserId;
+ pEntry->PrimaryGroupId = AccountInfo->PrimaryGroupId;
+
+ length = pEntry->GroupCount = AccountInfo->GroupCount;
+ length *= sizeof(GROUP_MEMBERSHIP);
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->GroupIds, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ length = pEntry->LogonDomainNameLength = AccountInfo->LogonDomainName.Length;
+ if (length) {
+ RtlCopyMemory(dataptr, AccountInfo->LogonDomainName.Buffer, length);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+ }
+
+ if (AccountInfo->UserFlags & LOGON_EXTRA_SIDS) {
+ length = pEntry->SidCount = AccountInfo->SidCount;
+ length *= sizeof(ULONG);
+ if (length) {
+ ULONG i, sidLength;
+ PULONG sidAttributes = (PULONG) dataptr;
+
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ //
+ // Now copy over all the SIDs
+ //
+
+ for (i = 0; i < AccountInfo->SidCount ; i++ ) {
+ sidAttributes[i] = AccountInfo->ExtraSids[i].Attributes;
+ sidLength = RtlLengthSid(AccountInfo->ExtraSids[i].Sid);
+ RtlCopySid(sidLength,(PSID) dataptr,AccountInfo->ExtraSids[i].Sid);
+ dataptr = ROUND_UP_POINTER(dataptr + sidLength, sizeof(ULONG));
+ }
+ pEntry->SidLength = (ULONG) (dataptr - (PCHAR) sidAttributes);
+ } else {
+ pEntry->SidLength = 0;
+ }
+ } else {
+ pEntry->SidCount = 0;
+ pEntry->SidLength = 0;
+ }
+
+ pEntry->LogonDomainIdLength = (USHORT) RtlLengthSid(AccountInfo->LogonDomainId);
+
+ #if DBG
+ NtStatus = RtlCopySid(pEntry->LogonDomainIdLength,
+ (PSID)dataptr,
+ AccountInfo->LogonDomainId
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (DumpCacheInfo) {
+ DbgPrint("NlpBuildCacheEntry:\n");
+ DumpCacheEntry(999, pEntry);
+ }
+ #else
+ RtlCopySid(*pEntryLength - ((ULONG)dataptr - (ULONG)pEntry),
+ (PSID)dataptr,
+ AccountInfo->LogonDomainId
+ );
+ #endif
+
+
+ *ppCacheEntry = pEntry;
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("BuildCacheEntry:\n");
+ DumpCacheEntry(999,pEntry);
+ }
+#endif
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NlpOpenCache( VOID )
+
+/*++
+
+Routine Description:
+
+ Opens the registry node for read or write (depending on Switch) and opens
+ the secret storage in the same mode. If successful, the NlpCacheHandle
+ is valid.
+
+Arguments:
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ NlpCacheHandle contains handle to use for reading/writing
+ registry
+
+ Failure =
+
+--*/
+
+{
+ NTSTATUS NtStatus;
+ ULONG Disposition;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING ObjectName;
+
+ ObjectName.Length = ObjectName.MaximumLength = CACHE_NAME_SIZE;
+ ObjectName.Buffer = CACHE_NAME;
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &ObjectName,
+ OBJ_CASE_INSENSITIVE,
+ 0, // RootDirectory
+ NULL // BUGBUG - put security descriptor here
+ );
+ NtStatus = NtCreateKey(&NlpCacheHandle,
+ (KEY_WRITE | KEY_READ),
+ &ObjectAttributes,
+ CACHE_TITLE_INDEX,
+ NULL, // class name
+ 0, // create options
+ &Disposition
+ );
+
+ return NtStatus;
+}
+
+
+VOID
+NlpCloseCache( VOID )
+
+/*++
+
+Routine Description:
+
+ Closes handles opened by NlpOpenCache
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+#if DBG
+ NTSTATUS NtStatus;
+
+ if (DumpCacheInfo) {
+ DbgPrint("CloseCache: Closing NlpCacheHandle (%#08x)\n", NlpCacheHandle);
+ }
+
+ if (IS_VALID_HANDLE(NlpCacheHandle)) {
+ NtStatus = NtClose(NlpCacheHandle);
+ if (DumpCacheInfo) {
+ DbgPrint("CloseCache: NtClose returns %#08x\n", NtStatus);
+ }
+ ASSERT(NT_SUCCESS(NtStatus));
+ INVALIDATE_HANDLE(NlpCacheHandle);
+ }
+#else
+ if (IS_VALID_HANDLE(NlpCacheHandle)) {
+ NtClose(NlpCacheHandle);
+ INVALIDATE_HANDLE(NlpCacheHandle);
+ }
+#endif
+}
+
+
+NTSTATUS
+NlpOpenSecret(
+ IN ULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ Opens a cache entry's secret storage object for read (in order to LsaQuerySecret) and
+ write (in order to LsaSetSecret). If successful, the handle value
+ is placed in the global variable NlpSecretHandle.
+
+ If the secret does not exist, it will be created.
+
+
+Arguments:
+
+ Index - The index of the entry being opened. This is used to build
+ a name of the object.
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ NlpSecretHandle can be used to read/write secret storage
+
+ Failure =
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ ValueName;
+
+ WCHAR
+ NameBuffer[32];
+
+
+ //
+ // Close previous handle if necessary
+ //
+
+ if (IS_VALID_HANDLE(NlpSecretHandle)) {
+ LsaClose( NlpSecretHandle );
+ }
+
+
+ ValueName.Buffer = &NameBuffer[0];
+ ValueName.MaximumLength = 32;
+ ValueName.Length = 0;
+ NlpMakeCacheEntryName( Index, &ValueName );
+
+
+
+
+ NtStatus = LsaOpenSecret(NlpLsaHandle,
+ &ValueName,
+ SECRET_QUERY_VALUE | SECRET_SET_VALUE,
+ &NlpSecretHandle
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
+ NtStatus = LsaCreateSecret(NlpLsaHandle,
+ &ValueName,
+ SECRET_SET_VALUE | SECRET_QUERY_VALUE,
+ &NlpSecretHandle
+ );
+ if (!NT_SUCCESS(NtStatus)) {
+ INVALIDATE_HANDLE(NlpSecretHandle);
+ }
+ } else {
+ INVALIDATE_HANDLE(NlpSecretHandle);
+ }
+ }
+ return(NtStatus);
+}
+
+
+VOID
+NlpCloseSecret( VOID )
+
+/*++
+
+Routine Description:
+
+ Closes the handles opened via NlpOpenSecret
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ if (IS_VALID_HANDLE(NlpSecretHandle)) {
+ NtStatus = LsaClose(NlpSecretHandle);
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("CloseSecret: LsaClose returns %#08x\n", NtStatus);
+ }
+#endif
+ ASSERT(NT_SUCCESS(NtStatus));
+ INVALIDATE_HANDLE(NlpSecretHandle);
+ }
+}
+
+
+NTSTATUS
+NlpWriteSecret(
+ IN PUNICODE_STRING NewSecret,
+ IN PUNICODE_STRING OldSecret
+ )
+
+/*++
+
+Routine Description:
+
+ Writes the password (and optionally the previous password) to the LSA
+ secret storage
+
+Arguments:
+
+ NewSecret - pointer to UNICODE_STRING containing current password
+ OldSecret - pointer to UNICODE_STRING containing previous password
+
+Return Value:
+
+ NTSTATUS
+ Success =
+ Failure =
+
+--*/
+
+{
+
+ return LsaSetSecret(NlpSecretHandle, NewSecret, OldSecret);
+}
+
+
+NTSTATUS
+NlpReadSecret(
+ OUT PUNICODE_STRING* NewSecret,
+ OUT PUNICODE_STRING* OldSecret
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the new and old secrets (UNICODE_STRINGs) for the
+ currently open LSA secret
+
+ The Lsa routine returns us pointers to UNICODE strings
+
+Arguments:
+
+ NewSecret - pointer to returned pointer to UNICODE_STRING containing
+ most recent password (if any)
+
+ OldSecret - pointer to returned pointer to UNICODE_STRING containing
+ previous password (if any)
+
+Return Value:
+
+ NTSTATUS
+ Success
+ Failure
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ LARGE_INTEGER
+ NewTime,
+ OldTime;
+
+ NtStatus = LsaQuerySecret(NlpSecretHandle,
+ NewSecret,
+ &NewTime,
+ OldSecret,
+ &OldTime
+ );
+#if DBG
+ {
+ char newNt[80];
+ char newLm[80];
+ char oldNt[80];
+ char oldLm[80];
+
+ if (DumpCacheInfo) {
+ DbgPrint("NlpReadSecret: NewSecret.Nt = \"%s\"\n"
+ " NewSecret.Lm = \"%s\"\n"
+ " OldSecret.Nt = \"%s\"\n"
+ " OldSecret.Lm = \"%s\"\n",
+ *NewSecret
+ ? DumpOwfPasswordToString(newNt, (PLM_OWF_PASSWORD)((*NewSecret)->Buffer))
+ : "",
+ *NewSecret
+ ? DumpOwfPasswordToString(newLm, (PLM_OWF_PASSWORD)((*NewSecret)->Buffer)+1)
+ : "",
+ *OldSecret
+ ? DumpOwfPasswordToString(oldNt, (PLM_OWF_PASSWORD)((*OldSecret)->Buffer))
+ : "",
+ *OldSecret
+ ? DumpOwfPasswordToString(oldLm, (PLM_OWF_PASSWORD)((*OldSecret)->Buffer)+1)
+ : ""
+ );
+ }
+ }
+#endif
+
+ return NtStatus;
+}
+
+
+NTSTATUS
+NlpMakeSecretPassword(
+ OUT PUNICODE_STRING Passwords,
+ IN PNT_OWF_PASSWORD NtOwfPassword OPTIONAL,
+ IN PLM_OWF_PASSWORD LmOwfPassword OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a (fixed length structure) NT_OWF_PASSWORD and a LM_OWF_PASSWORD
+ to a UNICODE_STRING. Allocates memory for the unicode string in this function
+
+ The calling function must free up the string buffer allocated in this routine.
+ The caller uses FreeToHeap (RtlFreeHeap)
+
+Arguments:
+
+ Passwords - returned UNICODE_STRING which actually contains a
+ CACHE_PASSWORDS structure
+ NtOwfPassword - pointer to encrypted, fixed-length NT password
+ LmOwfPassword - pointer to encrypted, fixed-length LM password
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ Passwords created OK
+
+ Failure = STATUS_NO_MEMORY
+ Not enough storage to create Passwords
+
+--*/
+
+{
+ PCACHE_PASSWORDS pwd;
+
+ pwd = (PCACHE_PASSWORDS)AllocateFromHeap(sizeof(*pwd));
+ if (pwd == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // concatenate the fixed length NT_OWF_PASSWORD and LM_OWF_PASSWORD structures
+ // into a buffer which we then use as a UNICODE_STRING
+ //
+
+ if (ARGUMENT_PRESENT(NtOwfPassword)) {
+ RtlCopyMemory((PVOID)&pwd->SecretPasswords.NtOwfPassword,
+ (PVOID)NtOwfPassword,
+ sizeof(*NtOwfPassword)
+ );
+ pwd->SecretPasswords.NtPasswordPresent = TRUE;
+ } else {
+ RtlZeroMemory((PVOID)&pwd->SecretPasswords.NtOwfPassword,
+ sizeof(pwd->SecretPasswords.NtOwfPassword)
+ );
+ pwd->SecretPasswords.NtPasswordPresent = FALSE;
+ }
+
+ if (ARGUMENT_PRESENT(LmOwfPassword)) {
+ RtlCopyMemory((PVOID)&pwd->SecretPasswords.LmOwfPassword,
+ (PVOID)LmOwfPassword,
+ sizeof(*LmOwfPassword)
+ );
+ pwd->SecretPasswords.LmPasswordPresent = TRUE;
+ } else {
+ RtlZeroMemory((PVOID)&pwd->SecretPasswords.LmOwfPassword,
+ sizeof(pwd->SecretPasswords.LmOwfPassword)
+ );
+ pwd->SecretPasswords.LmPasswordPresent = FALSE;
+ }
+
+ Passwords->Length = Passwords->MaximumLength = sizeof(*pwd);
+ Passwords->Buffer = (PWSTR)pwd;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NlpReadCacheEntry(
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING UserName,
+ OUT PULONG Index,
+ OUT PLOGON_CACHE_ENTRY* CacheEntry,
+ OUT PULONG EntrySize
+ )
+
+/*++
+
+Routine Description:
+
+ Searches the active entry list for a domain\username
+ match in the cache. If a match is found, then it
+ is returned.
+
+Arguments:
+
+ DomainName - The name of the domain in which the account exists.
+
+ UserName - The name of the account whose password is to be changed.
+
+ Index - receives the index of the entry retrieved.
+
+ CacheEntry - pointer to place to return pointer to LOGON_CACHE_ENTRY
+
+ EntrySize - size of returned LOGON_CACHE_ENTRY
+
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ *ppEntry points to allocated LOGON_CACHE_ENTRY
+ *EntrySize is size of returned data
+
+ Failure = STATUS_NO_MEMORY
+ Couldn't allocate buffer for LOGON_CACHE_ENTRY
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ CachedUser,
+ CachedDomain;
+
+ PNLP_CTE
+ Next;
+
+
+ //
+ // Walk the active list looking for a domain/name match
+ //
+
+ Next = (PNLP_CTE)NlpActiveCtes.Flink;
+
+ while (Next != (PNLP_CTE)&NlpActiveCtes) {
+
+ NtStatus = NlpReadCacheEntryByIndex( Next->Index,
+ CacheEntry,
+ EntrySize
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ break; // out of while-loop
+ }
+
+
+
+ CachedUser.Length = (*CacheEntry)->UserNameLength;
+ CachedUser.MaximumLength = (*CacheEntry)->UserNameLength;
+
+ CachedDomain.Length = (*CacheEntry)->DomainNameLength;
+ CachedDomain.MaximumLength = (*CacheEntry)->DomainNameLength;
+
+ if ((*CacheEntry)->Revision == NLP_CACHE_REVISION) {
+ CachedUser.Buffer = (PWSTR)((*CacheEntry) + 1);
+ } else {
+ ASSERT((*CacheEntry)->Revision == NLP_CACHE_REVISION_NT_1_0A);
+ CachedUser.Buffer = (PWSTR) ((PBYTE) *CacheEntry + sizeof(LOGON_CACHE_ENTRY_1_0A));
+ }
+
+ CachedDomain.Buffer = (PWSTR)((LPBYTE)CachedUser.Buffer +
+ ROUND_UP_COUNT((*CacheEntry)->UserNameLength, sizeof(ULONG)));
+
+ if (RtlEqualUnicodeString(UserName, &CachedUser, (BOOLEAN) TRUE ) &&
+ RtlEqualDomainName(DomainName, &CachedDomain )) {
+
+
+ //
+ // found it !
+ //
+
+ break; // out of while-loop
+
+ }
+
+ //
+ // Not the right entry, free the registry structure
+ // and go on to the next one.
+ //
+
+ FreeToHeap( (*CacheEntry) );
+
+ Next = (PNLP_CTE)(Next->Link.Flink);
+ }
+
+ if (Next != (PNLP_CTE)&NlpActiveCtes && NT_SUCCESS(NtStatus)) {
+
+ //
+ // We found a match - Open the corresponding secret
+ //
+
+ (*Index) = Next->Index;
+ NtStatus = NlpOpenSecret(Next->Index);
+
+
+ if (!NT_SUCCESS(NtStatus)) {
+ FreeToHeap( (*CacheEntry) );
+ return(NtStatus);
+ }
+ } else {
+ NtStatus = STATUS_LOGON_FAILURE;
+ }
+
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpWriteCacheEntry(
+ IN ULONG Index,
+ IN PLOGON_CACHE_ENTRY Entry,
+ IN ULONG EntrySize
+ )
+
+/*++
+
+Routine Description:
+
+ Writes a LOGON_CACHE_ENTRY to the registry cache.
+
+ It is the caller's responsibility to place the corresponding
+ CTE table entry in the correct active/inactive list.
+
+Arguments:
+ Index - Index of entry to write out.
+
+ Entry - pointer to LOGON_CACHE_ENTRY to write to cache
+
+ EntrySize - size of this entry (in bytes (must be multiple of 4 thoough))
+
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ The LOGON_CACHE_ENTRY is now in the registry (hopefully
+ on disk)
+
+ Failure =
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ ValueName;
+
+ WCHAR
+ NameBuffer[32];
+
+ ValueName.MaximumLength = 32;
+ ValueName.Length = 0;
+ ValueName.Buffer = &NameBuffer[0];
+ NlpMakeCacheEntryName( Index, &ValueName );
+
+ NtStatus = NtSetValueKey(NlpCacheHandle,
+ &ValueName,
+ 0, // TitleIndex
+ REG_BINARY, // Type
+ (PVOID)Entry,
+ EntrySize
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ NtFlushKey(NlpCacheHandle);
+ }
+ return(NtStatus);
+}
+
+
+VOID
+NlpCopyAndUpdateAccountInfo(
+ IN USHORT Length,
+ IN PUNICODE_STRING pUnicodeString,
+ IN OUT PUCHAR* pSource,
+ IN OUT PUCHAR* pDest
+ )
+
+/*++
+
+Routine Description:
+
+ Updates a UNICODE_STRING structure and copies the associated buffer to
+ *pDest, if Length is non-zero
+
+Arguments:
+
+ Length - length of UNICODE_STRING.Buffer to copy
+ pUnicodeString - pointer to UNICODE_STRING structure to update
+ pSource - pointer to pointer to source WCHAR string
+ pDest - pointer to pointer to place to copy WCHAR string
+
+Return Value:
+
+ None.
+ if string was copied, *Source and *Dest are updated to point to the next
+ naturally aligned (DWORD) positions in the input and output buffers resp.
+
+--*/
+
+{
+ PUCHAR source = *pSource;
+ PUCHAR dest = *pDest;
+
+ pUnicodeString->Length = Length;
+ pUnicodeString->MaximumLength = Length;
+ pUnicodeString->Buffer = (PWSTR)dest;
+ if (Length) {
+ RtlCopyMemory(dest, source, Length);
+ *pSource = ROUND_UP_POINTER(source + Length, sizeof(ULONG));
+ *pDest = ROUND_UP_POINTER(dest + Length, sizeof(ULONG));
+ }
+}
+
+
+VOID
+NlpSetTimeField(
+ OUT POLD_LARGE_INTEGER pTimeField,
+ IN NLP_SET_TIME_HINT Hint
+ )
+
+/*++
+
+Routine Description:
+
+ Sets a LARGE_INTEGER time field to one of 3 values:
+ NLP_BIG_TIME = maximum positive large integer (0x7fffffffffffffff)
+ NLP_SMALL_TIME = smallest positive large integer (0)
+ NLP_NOW_TIME = current system time
+
+Arguments:
+
+ pTimeField - pointer to LARGE_INTEGER structure to update
+ Hint - NLP_BIG_TIME, NLP_SMALL_TIME or NLP_NOW_TIME
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LARGE_INTEGER Time;
+
+ switch (Hint) {
+ case NLP_SMALL_TIME:
+ pTimeField->HighPart = SMALL_PART_1;
+ pTimeField->LowPart = SMALL_PART_2;
+ break;
+
+ case NLP_BIG_TIME:
+ pTimeField->HighPart = BIG_PART_1;
+ pTimeField->LowPart = BIG_PART_2;
+ break;
+
+ case NLP_NOW_TIME:
+ NtQuerySystemTime(&Time);
+ NEW_TO_OLD_LARGE_INTEGER( Time, (*pTimeField) );
+ break;
+ }
+}
+
+
+NTSTATUS
+NlpBuildAccountInfo(
+ IN PLOGON_CACHE_ENTRY pCacheEntry,
+ IN ULONG EntryLength,
+ OUT PNETLOGON_VALIDATION_SAM_INFO2* AccountInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Performs the reverse of NlpBuildCacheEntry - creates a NETLOGON_VALIDATION_SAM_INFO2
+ structure from a cache entry
+
+Arguments:
+
+ pCacheEntry - pointer to LOGON_CACHE_ENTRY
+
+ EntryLength - inclusive size of *pCacheEntry, including variable data
+
+ AccountInfo - pointer to place to create NETLOGON_VALIDATION_SAM_INFO2
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+
+ Failure = STATUS_NO_MEMORY
+
+--*/
+
+{
+ PNETLOGON_VALIDATION_SAM_INFO2 pSamInfo;
+ PUCHAR source;
+ PUCHAR dest;
+ ULONG length;
+ ULONG sidLength;
+ ULONG commonBits;
+ WCHAR computerName[CNLEN+1];
+ ULONG computerNameLength;
+#if DBG
+ BOOL ok;
+#endif
+
+ computerName[0] = 0;
+ computerNameLength = sizeof(computerName);
+
+ //
+ // will GetComputerName ever fail??? Its only used to fake the logon
+ // server name when we logon using the cached information, so its
+ // probably ok to use a NULL string if GetComputerName phones in sick
+ //
+
+#if DBG
+ ok =
+#endif
+
+ GetComputerName((LPWSTR)computerName, (LPDWORD)&computerNameLength);
+
+#if DBG
+ ASSERT(ok);
+ if (DumpCacheInfo) {
+ DbgPrint("ComputerName = %ws, length = %d\n", computerName, computerNameLength);
+ }
+#endif
+
+ //
+ // commonBits is the size of the variable data area common to both the
+ // LOGON_CACHE_ENTRY and NETLOGON_VALIDATION_SAM_INFO2 structures
+ //
+
+ commonBits = ROUND_UP_COUNT(pCacheEntry->EffectiveNameLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->FullNameLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->LogonScriptLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->ProfilePathLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->HomeDirectoryLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->HomeDirectoryDriveLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->GroupCount * sizeof(GROUP_MEMBERSHIP), sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->LogonDomainNameLength, sizeof(ULONG))
+ ;
+ if (pCacheEntry->Revision == NLP_CACHE_REVISION) {
+
+ commonBits +=
+ ROUND_UP_COUNT(sizeof(NETLOGON_SID_AND_ATTRIBUTES) * pCacheEntry->SidCount, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->SidLength, sizeof(ULONG))
+ ;
+ sidLength = pCacheEntry->LogonDomainIdLength;
+
+ } else {
+
+ ASSERT(pCacheEntry->Revision == NLP_CACHE_REVISION_NT_1_0A);
+
+ //
+ // sidLength is the size of the SID copied to the LOGON_CACHE_ENTRY
+ // structure
+ //
+
+ sidLength = EntryLength - (sizeof(LOGON_CACHE_ENTRY_1_0A)
+ + ROUND_UP_COUNT(pCacheEntry->UserNameLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->DomainNameLength, sizeof(ULONG))
+ + commonBits
+ );
+
+ }
+
+
+
+ //
+ // length is the required amount of buffer in which to build a working
+ // NETLOGON_VALIDATION_SAM_INFO2 structure
+ //
+
+ length = ROUND_UP_COUNT(sizeof(NETLOGON_VALIDATION_SAM_INFO2), sizeof(ULONG))
+ + commonBits
+ + sidLength
+ + computerNameLength * sizeof(WCHAR)
+ ;
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpBuildAccountInfo: %d bytes required\n", length);
+ }
+#endif
+
+ pSamInfo = (PNETLOGON_VALIDATION_SAM_INFO2)MIDL_user_allocate(length);
+ if (pSamInfo == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // point source at the first string to be copied out of the variable length
+ // data area at the end of the cache entry
+ //
+
+ if (pCacheEntry->Revision == NLP_CACHE_REVISION) {
+ source = (PUCHAR)(pCacheEntry + 1);
+ } else {
+ ASSERT(pCacheEntry->Revision == NLP_CACHE_REVISION_NT_1_0A);
+
+ source = (PUCHAR) pCacheEntry + sizeof(LOGON_CACHE_ENTRY_1_0A);
+ }
+
+ source += ROUND_UP_COUNT(pCacheEntry->UserNameLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(pCacheEntry->DomainNameLength, sizeof(ULONG));
+
+ //
+ // point dest at the first (aligned) byte at the start of the variable data
+ // area at the end of the sam info structure
+ //
+
+ dest = (PUCHAR)(pSamInfo + 1);
+
+ //
+ // pull out the variable length data from the end of the LOGON_CACHE_ENTRY
+ // and stick them at the end of the NETLOGON_VALIDATION_SAM_INFO2 structure.
+ // These must be copied out IN THE SAME ORDER as NlpBuildCacheEntry put them
+ // in. If we want to change the order of things in the buffer, the order
+ // must be changed in both routines (this & NlpBuildCacheEntry)
+ //
+
+ //
+ // create the UNICODE_STRING structures in the NETLOGON_VALIDATION_SAM_INFO2
+ // structure and copy the strings to the end of the buffer. 0 length strings
+ // will get a pointer which should be ignored
+ //
+
+ NlpCopyAndUpdateAccountInfo(pCacheEntry->EffectiveNameLength,
+ &pSamInfo->EffectiveName,
+ &source,
+ &dest
+ );
+
+ NlpCopyAndUpdateAccountInfo(pCacheEntry->FullNameLength,
+ &pSamInfo->FullName,
+ &source,
+ &dest
+ );
+
+ NlpCopyAndUpdateAccountInfo(pCacheEntry->LogonScriptLength,
+ &pSamInfo->LogonScript,
+ &source,
+ &dest
+ );
+
+ NlpCopyAndUpdateAccountInfo(pCacheEntry->ProfilePathLength,
+ &pSamInfo->ProfilePath,
+ &source,
+ &dest
+ );
+
+ NlpCopyAndUpdateAccountInfo(pCacheEntry->HomeDirectoryLength,
+ &pSamInfo->HomeDirectory,
+ &source,
+ &dest
+ );
+
+ NlpCopyAndUpdateAccountInfo(pCacheEntry->HomeDirectoryDriveLength,
+ &pSamInfo->HomeDirectoryDrive,
+ &source,
+ &dest
+ );
+
+ //
+ // copy the group membership array
+ //
+
+ pSamInfo->GroupIds = (PGROUP_MEMBERSHIP)dest;
+ length = pCacheEntry->GroupCount * sizeof(GROUP_MEMBERSHIP);
+ RtlCopyMemory(dest, source, length);
+ dest = ROUND_UP_POINTER(dest + length, sizeof(ULONG));
+ source = ROUND_UP_POINTER(source + length, sizeof(ULONG));
+
+ //
+ // final UNICODE_STRING from LOGON_CACHE_ENTRY. Reorganize this to:
+ // strings, groups, SID?
+ //
+
+ NlpCopyAndUpdateAccountInfo(pCacheEntry->LogonDomainNameLength,
+ &pSamInfo->LogonDomainName,
+ &source,
+ &dest
+ );
+
+
+ //
+ // Copy all the SIDs
+ //
+
+ if (pCacheEntry->Revision == NLP_CACHE_REVISION) {
+ pSamInfo->SidCount = pCacheEntry->SidCount;
+
+ if (pCacheEntry->SidCount) {
+ ULONG i, sidLength;
+ PULONG SidAttributes = (PULONG) source;
+ source = ROUND_UP_POINTER(source + pCacheEntry->SidCount * sizeof(ULONG), sizeof(ULONG));
+
+ pSamInfo->ExtraSids = (PNETLOGON_SID_AND_ATTRIBUTES) dest;
+ dest = ROUND_UP_POINTER(dest + pCacheEntry->SidCount * sizeof(NETLOGON_SID_AND_ATTRIBUTES), sizeof(ULONG));
+
+ for (i = 0; i < pCacheEntry->SidCount ; i++ ) {
+ pSamInfo->ExtraSids[i].Attributes = SidAttributes[i];
+ sidLength = RtlLengthSid((PSID) source);
+ RtlCopySid(sidLength, (PSID) dest, (PSID) source);
+ pSamInfo->ExtraSids[i].Sid = (PSID) dest;
+ dest = ROUND_UP_POINTER(dest + sidLength, sizeof(ULONG));
+ source = ROUND_UP_POINTER(source + sidLength, sizeof(ULONG));
+ }
+
+ ASSERT((ULONG) (source - (PCHAR) SidAttributes) == pCacheEntry->SidLength);
+
+ } else {
+ pSamInfo->ExtraSids = NULL;
+ }
+ } else {
+ pSamInfo->ExtraSids = NULL;
+ pSamInfo->SidCount = 0;
+ }
+
+
+ //
+ // copy the LogonDomainId SID
+ //
+
+ RtlCopySid(sidLength, (PSID)dest, (PSID)source);
+ pSamInfo->LogonDomainId = (PSID)dest;
+ dest = ROUND_UP_POINTER(dest + sidLength, sizeof(ULONG));
+
+ //
+ // copy the non-variable fields
+ //
+
+ pSamInfo->UserId = pCacheEntry->UserId;
+ pSamInfo->PrimaryGroupId = pCacheEntry->PrimaryGroupId;
+ pSamInfo->GroupCount = pCacheEntry->GroupCount;
+
+ //
+ // finally, invent some fields
+ //
+
+ NlpSetTimeField(&pSamInfo->LogonTime, NLP_NOW_TIME);
+ NlpSetTimeField(&pSamInfo->LogoffTime, NLP_BIG_TIME);
+ NlpSetTimeField(&pSamInfo->KickOffTime, NLP_BIG_TIME);
+ NlpSetTimeField(&pSamInfo->PasswordLastSet, NLP_SMALL_TIME);
+ NlpSetTimeField(&pSamInfo->PasswordCanChange, NLP_BIG_TIME);
+ NlpSetTimeField(&pSamInfo->PasswordMustChange, NLP_BIG_TIME);
+
+ pSamInfo->LogonCount = 0;
+ pSamInfo->BadPasswordCount = 0;
+ pSamInfo->UserFlags = LOGON_EXTRA_SIDS;
+
+ RtlZeroMemory(&pSamInfo->UserSessionKey, sizeof(pSamInfo->UserSessionKey));
+
+ //
+ // final UNICODE_STRING. This one from stack. Note that we have finished
+ // with source
+ //
+
+ source = (PUCHAR)computerName;
+ NlpCopyAndUpdateAccountInfo((USHORT)(computerNameLength * sizeof(WCHAR)),
+ &pSamInfo->LogonServer,
+ &source,
+ &dest
+ );
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpBuildAccountInfo:\n");
+ DumpAccountInfo(pSamInfo);
+ }
+#endif
+
+ *AccountInfo = pSamInfo;
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NlpGetCacheControlInfo( VOID )
+
+/*++
+
+Routine Description:
+
+ This function retrieves the cache control information from the
+ registry. This information is placed in global data for use
+ throughout this module. The Cache Table Entry table will also
+ be initialized.
+
+ If this routine returns success, then it may be assumed that
+ everything completed successfully.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ CacheControlValueName;
+
+ ULONG
+ RequiredSize;
+
+ PKEY_VALUE_PARTIAL_INFORMATION
+ RegInfo;
+
+
+ //
+ // read the current control info, if it is there.
+ // If it is not there, then we may be dealing with a down-level
+ // system and might have a single cache entry in the registry.
+ //
+
+ RtlInitUnicodeString( &CacheControlValueName, L"NL$Control" );
+ NtStatus = NtQueryValueKey(NlpCacheHandle,
+ &CacheControlValueName,
+ KeyValuePartialInformation,
+ NULL,
+ 0,
+ &RequiredSize
+ );
+
+ if (NT_SUCCESS(NtStatus) || NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
+ NTSTATUS TempStatus;
+
+ //
+ // Hmmm - no entry, that means we are dealing with a
+ // first release system here (that didn't have
+ // this value).
+ //
+
+
+ //
+ // Set up for 1 cache entry.
+ // create the secret and cache key entry
+ //
+
+ TempStatus = NlpMakeNewCacheEntry( 0 );
+
+ if ( NT_SUCCESS(TempStatus) ) {
+ //
+ // Now flush out the control information
+ //
+
+
+ NlpCacheControl.Revision = NLP_CACHE_REVISION;
+ NlpCacheControl.Entries = 1;
+ TempStatus = NlpWriteCacheControl();
+
+ if ( NT_SUCCESS(TempStatus) ) {
+
+ //
+ // If a version 1.0 entry exists,
+ // copy the old form of cache entry to the new structure.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+ TempStatus = NlpConvert1_0To1_0B();
+ }
+ }
+ }
+
+ NtStatus = TempStatus;
+
+ } else if ( NtStatus == STATUS_BUFFER_TOO_SMALL ) {
+
+ //
+ // allocate buffer then do query again, this time receiving data
+ //
+
+ RegInfo = (PKEY_VALUE_PARTIAL_INFORMATION)AllocateFromHeap(RequiredSize);
+ if (RegInfo == NULL) {
+ NlpCacheControl.Entries = 0;
+ return(STATUS_NO_MEMORY);
+ }
+
+ NtStatus = NtQueryValueKey(NlpCacheHandle,
+ &CacheControlValueName,
+ KeyValuePartialInformation,
+ (PVOID)RegInfo,
+ RequiredSize,
+ &RequiredSize
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ NlpCacheControl.Entries = 0;
+ return(NtStatus);
+ }
+
+ //
+ // check the revision - we can't deal with up-level revisions.
+ //
+
+ if (RegInfo->DataLength < sizeof(NLP_CACHE_CONTROL)) {
+ NlpCacheControl.Entries = 0; // Disable caching
+ return(STATUS_UNKNOWN_REVISION);
+ }
+
+ RtlMoveMemory( &NlpCacheControl, &(RegInfo->Data[0]), sizeof(NLP_CACHE_CONTROL) );
+ if (NlpCacheControl.Revision > NLP_CACHE_REVISION) {
+ NlpCacheControl.Entries = 0; // Disable caching
+ return(STATUS_UNKNOWN_REVISION);
+ }
+
+ FreeToHeap( RegInfo );
+
+ //
+ // If this is an older cache, update it with the latest revision
+ //
+
+ if (NlpCacheControl.Revision != NLP_CACHE_REVISION) {
+
+ NlpCacheControl.Revision = NLP_CACHE_REVISION;
+ NtStatus = NlpWriteCacheControl();
+
+ if (!NT_SUCCESS(NtStatus)) {
+ NlpCacheControl.Entries = 0;
+ return(NtStatus);
+ }
+ }
+
+ return(STATUS_SUCCESS);
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ NlpCacheControl.Entries = 0; // Disable logon cache
+ }
+
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpBuildCteTable( VOID )
+
+/*++
+
+Routine Description:
+
+ This function initializes the CTE table from the contents of
+ the cache in the registry.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - the cache is initialized.
+
+ Other - The cache has been disabled.
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ PLOGON_CACHE_ENTRY
+ CacheEntry;
+
+ ULONG
+ EntrySize,
+ i;
+
+
+ //
+ // Initialize the active and inactive CTE lists
+ //
+
+ InitializeListHead( &NlpActiveCtes );
+ InitializeListHead( &NlpInactiveCtes );
+
+
+ //
+ // Allocate a CTE table
+ //
+
+ NlpCteTable = AllocateFromHeap( sizeof( NLP_CTE ) *
+ NlpCacheControl.Entries );
+ if (NlpCteTable == NULL) {
+
+ //
+ // Can't allocate table, disable caching
+ //
+
+ NlpCacheControl.Entries = 0; // Disable cache
+ return(STATUS_NO_MEMORY);
+ }
+
+ for (i=0; i<NlpCacheControl.Entries; i++) {
+
+ NtStatus = NlpReadCacheEntryByIndex( i,
+ &CacheEntry,
+ &EntrySize);
+ if (!NT_SUCCESS(NtStatus) ) {
+ NlpCacheControl.Entries = 0; // Disable cache
+ return(NtStatus);
+ }
+
+ //
+ //
+ if (EntrySize < sizeof(LOGON_CACHE_ENTRY_1_0A)) {
+
+ //
+ // Hmmm, something is bad.
+ // disable caching and return an error
+ //
+
+ NlpCacheControl.Entries = 0; // Disable cache
+ FreeToHeap( CacheEntry );
+ return( STATUS_INTERNAL_DB_CORRUPTION );
+ }
+
+ if (CacheEntry->Revision > NLP_CACHE_REVISION) {
+ NlpCacheControl.Entries = 0; // Disable cache
+ FreeToHeap( CacheEntry );
+ return(STATUS_UNKNOWN_REVISION);
+ }
+
+ NlpCteTable[i].Index = i;
+ NlpCteTable[i].Active = CacheEntry->Valid;
+ NlpCteTable[i].Time = CacheEntry->Time;
+
+ InsertTailList( &NlpInactiveCtes, &NlpCteTable[i].Link );
+
+ if (NlpCteTable[i].Active) {
+ NlpAddEntryToActiveList( i );
+ }
+
+ FreeToHeap( CacheEntry );
+
+ }
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpChangeCacheSizeIfNecessary( VOID )
+
+/*++
+
+Routine Description:
+
+ This function checks to see if the user has requested a
+ different cache size than what we currently have.
+
+ If so, then we try to grow or shrink our cache appropriately.
+ If this succeeds, then the global cache control information is
+ updated appropriately. If it fails then one of two things will
+ happen:
+
+ 1) If the user was trying to shrink the cache, then it will
+ be disabled (entries set to zero).
+
+ 2) If the user was trying to grow the cache, then we will leave
+ it as it is.
+
+ In either of these two failure conditions, an error is returned.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+
+ NTSTATUS
+ NtStatus;
+
+ UINT
+ CachedLogonsCount;
+
+ PNLP_CTE
+ NewCteTable,
+ Next;
+
+ LIST_ENTRY
+ NewActive,
+ NewInactive;
+
+ PNLP_CACHE_AND_SECRETS
+ CacheAndSecrets;
+
+
+ ULONG
+ ErrorCacheSize,
+ EntrySize,
+ i,
+ j;
+
+
+ // Find out how many logons to cache.
+ // This is a user setable value and it may be different than
+ // the last time we booted.
+ //
+
+ CachedLogonsCount = GetProfileInt(
+ TEXT("Winlogon"),
+ TEXT("CachedLogonsCount"),
+ NLP_DEFAULT_LOGON_CACHE_COUNT // Default value
+ );
+
+ //
+ // Minimize the user-supplied value with the maximum allowable
+ // value.
+ //
+
+ if (CachedLogonsCount > NLP_MAX_LOGON_CACHE_COUNT) {
+ CachedLogonsCount = NLP_MAX_LOGON_CACHE_COUNT;
+ }
+
+
+ //
+ // Compare it to what we already have and see if we need
+ // to change the size of the cache
+ //
+
+ if (CachedLogonsCount == NlpCacheControl.Entries) {
+
+ //
+ // No change
+ //
+
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // Set the size of the cache to be used in case of error
+ // changing the size. If we are trying to grow the cache,
+ // then use the existing cache on error. If we are trying
+ // to shrink the cache, then disable caching on error.
+ //
+
+ if (CachedLogonsCount > NlpCacheControl.Entries) {
+ ErrorCacheSize = NlpCacheControl.Entries;
+ } else {
+ ErrorCacheSize = 0;
+ }
+
+ //
+ // Allocate a CTE table the size of the new table
+ //
+
+ NewCteTable = AllocateFromHeap( sizeof( NLP_CTE ) *
+ CachedLogonsCount );
+ if (NewCteTable == NULL) {
+
+ //
+ // Can't shrink table, disable caching
+ //
+
+ NlpCacheControl.Entries = ErrorCacheSize;
+ return(STATUS_NO_MEMORY);
+ }
+
+
+
+ //
+ // Now the tricky parts ...
+ //
+
+ if (CachedLogonsCount > NlpCacheControl.Entries) {
+
+
+ //
+ // Try to grow the cache -
+ // Create additional secrets and cache entries.
+ //
+ // Copy time fields and set index
+ //
+
+ for (i=0; i < NlpCacheControl.Entries; i++) {
+ NewCteTable[i].Index = i;
+ NewCteTable[i].Time = NlpCteTable[i].Time;
+ }
+
+ //
+ // Place existing entries on either the active or inactive list
+ //
+
+ InitializeListHead( &NewActive );
+ for (Next = (PNLP_CTE)NlpActiveCtes.Flink;
+ Next != (PNLP_CTE)(&NlpActiveCtes);
+ Next = (PNLP_CTE)Next->Link.Flink
+ ) {
+
+ InsertTailList( &NewActive, &NewCteTable[Next->Index].Link );
+ NewCteTable[Next->Index].Active = TRUE;
+ }
+
+
+ InitializeListHead( &NewInactive );
+ for (Next = (PNLP_CTE)NlpInactiveCtes.Flink;
+ Next != (PNLP_CTE)(&NlpInactiveCtes);
+ Next = (PNLP_CTE)Next->Link.Flink
+ ) {
+
+ InsertTailList( &NewInactive, &NewCteTable[Next->Index].Link );
+ NewCteTable[Next->Index].Active = FALSE;
+ }
+
+
+ //
+ // Make all the new table entries.
+ // Mark them as invalid.
+ //
+
+ for (i=NlpCacheControl.Entries; i<CachedLogonsCount; i++) {
+
+ //
+ // Add the CTE entry to the inactive list
+ //
+
+ InsertTailList( &NewInactive, &NewCteTable[i].Link );
+ NewCteTable[i].Active = FALSE;
+ NewCteTable[i].Index = i;
+
+ NtStatus = NlpMakeNewCacheEntry( i );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ FreeToHeap( NewCteTable );
+ return(NtStatus);
+ }
+ }
+
+
+
+
+ } else {
+
+
+ //
+ // Try to shrink the cache.
+ //
+
+ if (CachedLogonsCount != 0) {
+
+ //
+ // 0 size implies disabling the cache.
+ // That is a degenerate case of shrinking that
+ // requires only the last few steps of shrinking.
+ //
+
+
+ //
+ // Allocate an array of pointers for reading registry and secret
+ // info into. Clear it to assist in cleanup.
+ //
+
+ CacheAndSecrets = (PNLP_CACHE_AND_SECRETS)
+ AllocateFromHeap( sizeof( NLP_CACHE_AND_SECRETS ) *
+ CachedLogonsCount );
+
+ if (CacheAndSecrets == NULL) {
+ FreeToHeap( NlpCteTable );
+ NlpCacheControl.Entries = ErrorCacheSize;
+ return(STATUS_NO_MEMORY);
+ }
+ RtlZeroMemory( CacheAndSecrets,
+ (sizeof( NLP_CACHE_AND_SECRETS ) * CachedLogonsCount) );
+
+
+ //
+ // Set up the new CTE table to be inactive
+ //
+
+ InitializeListHead( &NewActive );
+ InitializeListHead( &NewInactive );
+ for (i=0; i<CachedLogonsCount; i++) {
+ InsertTailList( &NewInactive, &NewCteTable[i].Link );
+ NewCteTable[i].Index = i;
+ NewCteTable[i].Active = FALSE;
+ }
+
+
+ //
+ // Walk the current active list, reading
+ // entries and copying information into the new CTE table.
+ //
+
+ i = 0;
+ Next = (PNLP_CTE)NlpActiveCtes.Flink;
+ while (Next != (PNLP_CTE)&NlpActiveCtes && i<CachedLogonsCount) {
+
+ NtStatus = NlpReadCacheEntryByIndex( Next->Index,
+ &CacheAndSecrets[i].CacheEntry,
+ &EntrySize
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpOpenSecret( Next->Index );
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpReadSecret( &CacheAndSecrets[i].NewSecret,
+ &CacheAndSecrets[i].OldSecret);
+
+ if (NT_SUCCESS(NtStatus)) {
+ //
+ // Only make this entry active if everything was
+ // successfully read in.
+ //
+
+ CacheAndSecrets[i].Active = TRUE;
+ i++; // advance our new CTE table index
+
+ }
+ NlpCloseSecret();
+ }
+ }
+
+ Next = (PNLP_CTE)(Next->Link.Flink);
+
+ } // end-while
+
+ //
+ // At this point "i" indicates how many CacheAndSecrets entries
+ // are active. Furthermore, the entries were assembled
+ // in the CacheAndSecrets array in ascending time order, which
+ // is the order they need to be placed in the new CTE table.
+ //
+
+ for ( j=0; j<i; j++) {
+
+ Next = &NewCteTable[j];
+
+ //
+ // The Time field in the original cache entry is not aligned
+ // properly, so copy each field individually.
+ //
+
+ Next->Time.LowPart = CacheAndSecrets[j].CacheEntry->Time.LowPart;
+ Next->Time.HighPart = CacheAndSecrets[j].CacheEntry->Time.HighPart;
+
+ //
+ // Try writing out the new entry's information
+ //
+
+ NtStatus = NlpWriteCacheEntry( j,
+ CacheAndSecrets[j].CacheEntry,
+ CacheAndSecrets[j].EntrySize
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpOpenSecret( j );
+
+ if (NT_SUCCESS(NtStatus)) {
+ NtStatus = NlpWriteSecret(CacheAndSecrets[j].NewSecret,
+ CacheAndSecrets[j].OldSecret);
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // move the corresponding entry into the new CTEs
+ // active list.
+ //
+
+ Next->Active = TRUE;
+ RemoveEntryList( &Next->Link );
+ InsertTailList( &NewActive, &Next->Link );
+
+ }
+ }
+ }
+
+ //
+ // Free the CacheEntry and secret information
+ //
+
+ if (CacheAndSecrets[j].CacheEntry != NULL) {
+ FreeToHeap( CacheAndSecrets[j].CacheEntry );
+ }
+ if (CacheAndSecrets[j].NewSecret != NULL) {
+ MIDL_user_free( CacheAndSecrets[j].NewSecret );
+ }
+ if (CacheAndSecrets[j].OldSecret != NULL) {
+ MIDL_user_free( CacheAndSecrets[j].OldSecret );
+ }
+ }
+
+ //
+ // Free the CacheAndSecrets array
+ // (everything in it has already been freed)
+ //
+
+ if (CacheAndSecrets != NULL) {
+ FreeToHeap( CacheAndSecrets );
+ }
+
+ //
+ // Change remaining entries to invalid (on disk)
+ //
+
+ for ( j=i; j<CachedLogonsCount; j++) {
+ NlpMakeNewCacheEntry( j );
+ }
+
+ } // end-if (CachedLogonsCount != 0)
+
+
+ //
+ // Now get rid of extra (no longer needed) entries
+ //
+
+ for ( j=CachedLogonsCount; j<NlpCacheControl.Entries; j++) {
+ NlpEliminateCacheEntry( j );
+ }
+
+
+ }
+
+ //
+ // We have successfully:
+ //
+ // Allocated the new CTE table.
+ //
+ // Filled the CTE table with copies of the currently
+ // active CTEs (including putting each CTE on an active
+ // or inactive list).
+ //
+ // Established new CTE entries, including the corresponding
+ // secrets and cache keys in the registry, for the
+ // new CTEs.
+ //
+ //
+ // All we have left to do is:
+ //
+ //
+ // Update the cache control structure in the registry
+ // to indicate we have a new length
+ //
+ // move the new CTE over to the real Active and Inactive
+ // list heads (rather than the local ones we've used so far)
+ //
+ // deallocate the old CTE table.
+ //
+ // Re-set the entries count in the in-memory
+ // cache-control structure NlpCacheControl.
+ //
+
+
+ NlpCacheControl.Entries = CachedLogonsCount;
+ NtStatus = NlpWriteCacheControl();
+
+ if (CachedLogonsCount > 0) { // Only necessary if there is a new CTE table
+ if (!NT_SUCCESS(NtStatus)) {
+ FreeToHeap( NewCteTable );
+ NlpCacheControl.Entries = ErrorCacheSize;
+ return(NtStatus);
+ }
+
+ InsertHeadList( &NewActive, &NlpActiveCtes );
+ RemoveEntryList( &NewActive );
+ InsertHeadList( &NewInactive, &NlpInactiveCtes );
+ RemoveEntryList( &NewInactive );
+
+ FreeToHeap( NlpCteTable );
+ NlpCteTable = NewCteTable;
+ }
+
+ return(NtStatus);
+
+}
+
+
+
+NTSTATUS
+NlpWriteCacheControl( VOID )
+
+/*++
+
+Routine Description:
+
+ This function writes a new cache length out to the
+ cache control structure stored in the registry.
+
+ Note:
+ When lengthening the cache, call this routine after the cache
+ entries and corresponding secrets have been established for
+ the new length.
+
+ When shortening the cache, call this routine before the cache
+ entries and corresponding secrets being discarded have actually
+ been discarded.
+
+ This ensures that if the system crashes during the resizing
+ operation, it will be in a valid state when the system comes
+ back up.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ CacheControlValueName;
+
+
+ RtlInitUnicodeString( &CacheControlValueName, L"NL$Control" );
+ NtStatus = NtSetValueKey( NlpCacheHandle,
+ &CacheControlValueName, // Name
+ 0, // TitleIndex
+ REG_BINARY, // Type
+ &NlpCacheControl, // Data
+ sizeof(NLP_CACHE_CONTROL) // DataLength
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ NtFlushKey( NlpCacheHandle );
+ }
+ return(NtStatus);
+}
+
+
+VOID
+NlpMakeCacheEntryName(
+ IN ULONG EntryIndex,
+ OUT PUNICODE_STRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a name of a cache entry value or secret name
+ for a cached entry. The name is based upon the index of the cache
+ entry.
+
+ Names are of the form:
+
+ "NLP1" through "NLPnnn"
+
+ where "nnn" is the largest allowable entry count (see
+ NLP_MAX_LOGON_CACHE_COUNT).
+
+ The output UNICODE_STRING buffer is expected to be large enough
+ to accept this string with a null termination on it.
+
+
+Arguments:
+
+ EntryIndex - The index of the cache entry whose name is desired.
+
+ Name - A unicode string large enough to accept the name.
+
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ TmpString;
+
+ WCHAR
+ TmpStringBuffer[17];
+
+ ASSERT(Name->MaximumLength >= 7*sizeof(WCHAR) );
+ ASSERT( EntryIndex <= NLP_MAX_LOGON_CACHE_COUNT );
+
+ Name->Length = 0;
+ RtlAppendUnicodeToString( Name, L"NL$" );
+
+ TmpString.MaximumLength = 16;
+ TmpString.Length = 0;
+ TmpString.Buffer = TmpStringBuffer;
+ NtStatus = RtlIntegerToUnicodeString ( (EntryIndex+1), // make 1 based index
+ 10, // Base 10
+ &TmpString
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ RtlAppendUnicodeStringToString( Name, &TmpString );
+
+
+ return;
+}
+
+
+NTSTATUS
+NlpMakeNewCacheEntry(
+ ULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates a secret and a cache entry value for a
+ new cache entry with the specified index.
+
+ The secret handle is NOT left open.
+
+
+Arguments:
+
+ Index - The index of the cache entry whose name is desired.
+
+ Name - A unicode string large enough to accept the name.
+
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ LOGON_CACHE_ENTRY
+ Entry;
+
+ UNICODE_STRING
+ ValueName;
+
+ WCHAR
+ NameBuffer[32];
+
+ LSA_HANDLE
+ SecretHandle;
+
+ ValueName.Length = 0;
+ ValueName.MaximumLength = 32;
+ ValueName.Buffer = &NameBuffer[0];
+
+ NlpMakeCacheEntryName( Index, &ValueName );
+
+ //
+ // Create a secret object for the entry
+ //
+
+ NtStatus = LsaCreateSecret( NlpLsaHandle,
+ &ValueName,
+ SECRET_SET_VALUE,
+ &SecretHandle
+ );
+ if (NtStatus == STATUS_OBJECT_NAME_COLLISION) {
+ NtStatus = LsaOpenSecret( NlpLsaHandle,
+ &ValueName,
+ SECRET_SET_VALUE,
+ &SecretHandle
+ );
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ LsaSetSecret( SecretHandle, NULL, NULL); // Don't worry about it failing
+
+ LsaClose( SecretHandle );
+
+ //
+ // Create the cache entry marked as invalid
+ //
+
+ RtlZeroMemory( &Entry, sizeof(Entry) );
+ Entry.Revision = NLP_CACHE_REVISION;
+ Entry.Valid = FALSE;
+ NtStatus = NtSetValueKey( NlpCacheHandle,
+ &ValueName, // Name
+ 0, // TitleIndex
+ REG_BINARY, // Type
+ &Entry, // Data
+ sizeof(LOGON_CACHE_ENTRY) // DataLength
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ NtFlushKey( NlpCacheHandle );
+ }
+ }
+
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpEliminateCacheEntry(
+ IN ULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ Delete the registry value and secret object related to a
+ CTE entry.
+
+Arguments:
+
+ Index - The index of the entry whose value and secret are to
+ be deleted. This value is used only to build a name with
+ (not to reference the CTE table).
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ ValueName;
+
+ WCHAR
+ NameBuffer[32];
+
+ LSA_HANDLE
+ SecretHandle;
+
+
+ ValueName.Buffer = &NameBuffer[0];
+ ValueName.MaximumLength = 32;
+ ValueName.Length = 0;
+ NlpMakeCacheEntryName( Index, &ValueName );
+
+ NtStatus = LsaOpenSecret(NlpLsaHandle,
+ &ValueName,
+ DELETE,
+ &SecretHandle
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Deleting and object causes its handle to be closed
+ //
+
+ NtStatus = LsaDelete( SecretHandle );
+
+
+ //
+ // Now delete the registry value
+ //
+
+ NtStatus = NtDeleteValueKey( NlpCacheHandle, &ValueName );
+ }
+
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpConvert1_0To1_0B( VOID )
+
+/*++
+
+Routine Description:
+
+ This function retrieves the cache entry used in NT1.0 systems
+ and stores it (if found) in the zero'th CTE entry. It also
+ copies the secrets from 1.0 storage format to 1.0B format.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - if the entry was successfully upgraded, or if
+ it didn't exist.
+
+ STATUS_NO_MEMORY - if we couldn't allocate memory from heap.
+
+ other - unexpected error.
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ PKEY_VALUE_FULL_INFORMATION
+ RegistryStructure;
+
+ PLOGON_CACHE_ENTRY_1_0
+ CacheEntry;
+
+ PLOGON_CACHE_ENTRY
+ NewCacheEntry;
+
+ UNICODE_STRING
+ NullName;
+
+ PUNICODE_STRING
+ CurrentSecret = NULL,
+ OldSecret = NULL;
+
+ ULONG
+ RequiredSize,
+ VariableSize,
+ EntrySize,
+ NewSize;
+
+
+ PUCHAR
+ Source,
+ Dest;
+
+
+
+ //
+ // This should always try to return at least
+ // the KEY_VALUE_FULL_INFORMATION structure, even
+ // if there isn't data available.
+ //
+
+ RtlInitUnicodeString(&NullName, NULL);
+ NtStatus = NtQueryValueKey(NlpCacheHandle,
+ &NullName,
+ KeyValueFullInformation,
+ NULL,
+ 0,
+ &RequiredSize
+ );
+ ASSERT(!NT_SUCCESS(NtStatus));
+
+ if (NtStatus != STATUS_BUFFER_TOO_SMALL) {
+ return(NtStatus);
+ }
+
+ RegistryStructure = AllocateFromHeap( RequiredSize );
+ if (RegistryStructure == NULL) {
+ NtStatus = STATUS_NO_MEMORY;
+ } else {
+
+
+ NtStatus = NtQueryValueKey(NlpCacheHandle,
+ &NullName,
+ KeyValueFullInformation,
+ RegistryStructure,
+ RequiredSize,
+ &RequiredSize
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // If we didn't get any data in the query, then there
+ // wasn't a cache entry, don't do anything. Otherwise,
+ // copy it to the new scheme.
+ //
+
+ if (RequiredSize > sizeof(KEY_VALUE_FULL_INFORMATION)) {
+
+
+
+ //
+ // OK, we now have a NT1_0 cache entry.
+ // This is the same as a NT1_0A cache entry, except that
+ // the fields from SidCount onward are new to NT1_0A and
+ // so aren't present in the structure we just read in.
+ //
+ // Now the challange is to build a new Registry Structure
+ // that looks just like the one we just read in, but adds
+ // in these new fields.
+ //
+ // Warning - the fields of CacheEntry are not necessarily
+ // aligned nicely because the structure starts
+ // at a random offset inside a registry header.
+ // Avoid referencing CacheEntry fields.
+ //
+
+ CacheEntry = (PLOGON_CACHE_ENTRY_1_0)
+ ((PCHAR)(RegistryStructure) +
+ (RegistryStructure->DataOffset));
+ EntrySize = RegistryStructure->DataLength;
+
+ Source = (PUCHAR)(((PLOGON_CACHE_ENTRY_1_0)CacheEntry) + 1);
+
+
+ VariableSize = EntrySize -
+ ROUND_UP_COUNT(sizeof(LOGON_CACHE_ENTRY_1_0), sizeof(ULONG));
+
+ NewSize =
+ ROUND_UP_COUNT(sizeof(LOGON_CACHE_ENTRY), sizeof(ULONG)) +
+ VariableSize;
+
+ NewCacheEntry = (PLOGON_CACHE_ENTRY)AllocateFromHeap(NewSize);
+ if (NewCacheEntry == NULL) {
+ NtStatus = STATUS_NO_MEMORY;
+ } else {
+
+ RtlZeroMemory( NewCacheEntry, NewSize );
+
+ //
+ // Copy the fixed-length aspects of the original
+ // CacheEntry into the new cache entry.
+ //
+
+ RtlMoveMemory( NewCacheEntry,
+ CacheEntry,
+ sizeof(LOGON_CACHE_ENTRY_1_0) );
+
+
+ //
+ // We have to figure out the length of the LogonDomainId
+ //
+ {
+ ULONG commonBits, sidLength;
+
+ commonBits =
+ ROUND_UP_COUNT(NewCacheEntry->EffectiveNameLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->FullNameLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->LogonScriptLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->ProfilePathLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->HomeDirectoryLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->HomeDirectoryDriveLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->GroupCount * sizeof(GROUP_MEMBERSHIP), sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->LogonDomainNameLength, sizeof(ULONG)
+ );
+
+ //
+ // sidLength is the size of the SID copied to the LOGON_CACHE_ENTRY structure
+ //
+
+ sidLength = EntrySize - (sizeof(LOGON_CACHE_ENTRY_1_0)
+ + ROUND_UP_COUNT(NewCacheEntry->UserNameLength, sizeof(ULONG))
+ + ROUND_UP_COUNT(NewCacheEntry->DomainNameLength, sizeof(ULONG))
+ + commonBits
+ );
+
+ NewCacheEntry->LogonDomainIdLength = (USHORT) sidLength;
+ }
+
+
+ NtQuerySystemTime( &NewCacheEntry->Time );
+ NewCacheEntry->Revision = NLP_CACHE_REVISION;
+ NewCacheEntry->Valid = TRUE;
+
+ Dest = (PUCHAR)(((PLOGON_CACHE_ENTRY)NewCacheEntry) + 1);
+ RtlMoveMemory( Dest, Source, VariableSize );
+
+
+
+ //
+ // put out the secrets first so that if it fails
+ // we haven't already validated the cache entry.
+ // This is done by tricking the secret routines.
+ //
+
+
+ NtStatus = NlpOpen_Nt1_0_Secret();
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpReadSecret(&CurrentSecret, &OldSecret);
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Write out the secrets in the zero'th entry
+ //
+
+ NtStatus = NlpOpenSecret( 0 );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpWriteSecret( CurrentSecret, OldSecret);
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = NlpWriteCacheEntry(
+ 0,
+ NewCacheEntry,
+ NewSize
+ );
+ //
+ // The CTE table isn't built
+ // yet, so don't try to update
+ // this entry in the CTE.
+ }
+
+ }
+
+ //
+ // Free the secret buffers
+ //
+
+ if (CurrentSecret) {
+ MIDL_user_free(CurrentSecret);
+ }
+ if (OldSecret) {
+ MIDL_user_free(OldSecret);
+ }
+ }
+
+ NlpCloseSecret();
+ }
+
+ FreeToHeap( NewCacheEntry );
+ }
+ }
+ }
+
+ FreeToHeap( RegistryStructure );
+ }
+}
+
+
+NTSTATUS
+NlpOpen_Nt1_0_Secret( VOID )
+
+/*++
+
+Routine Description:
+
+ Opens the secret object for the cache entry of a NT1.0 system.
+ This is used to upgrade the system. The secret is opened for
+ query access.
+
+ If the secret does not exist, it is NOT created.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - The secret was successfully openned.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - The secret didn't exist. The handle
+ value is not valid.
+
+ Other error - the handle value is invalid.
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ SecretName;
+
+
+ //
+ // Close previous handle if necessary
+ //
+
+ if (IS_VALID_HANDLE(NlpSecretHandle)) {
+ LsaClose( NlpSecretHandle );
+ }
+
+ SecretName.Length = SecretName.MaximumLength = SECRET_NAME_SIZE;
+ SecretName.Buffer = SECRET_NAME;
+ NtStatus = LsaOpenSecret(NlpLsaHandle,
+ &SecretName,
+ SECRET_QUERY_VALUE,
+ &NlpSecretHandle
+ );
+ return(NtStatus);
+}
+
+
+NTSTATUS
+NlpReadCacheEntryByIndex(
+ IN ULONG Index,
+ OUT PLOGON_CACHE_ENTRY* CacheEntry,
+ OUT PULONG EntrySize
+ )
+
+/*++
+
+Routine Description:
+
+ Reads a cache entry from registry
+
+Arguments:
+
+ Index - CTE table index of the entry to open.
+ This is used to build the entry's value and secret names.
+
+ CacheEntry - pointer to place to return pointer to LOGON_CACHE_ENTRY
+
+ EntrySize - size of returned LOGON_CACHE_ENTRY
+
+
+Return Value:
+
+ NTSTATUS
+ Success = STATUS_SUCCESS
+ *ppEntry points to allocated LOGON_CACHE_ENTRY
+ *EntrySize is size of returned data
+
+ Failure = STATUS_NO_MEMORY
+ Couldn't allocate buffer for LOGON_CACHE_ENTRY
+
+--*/
+
+{
+ NTSTATUS
+ NtStatus;
+
+ UNICODE_STRING
+ ValueName;
+
+ WCHAR
+ NameBuffer[32];
+
+ ULONG
+ RequiredSize;
+
+ PKEY_VALUE_FULL_INFORMATION
+ RegInfo;
+
+ PLOGON_CACHE_ENTRY
+ RCacheEntry; // CacheEntry in registry buffer
+
+ ValueName.Buffer = &NameBuffer[0];
+ ValueName.MaximumLength = 32;
+ ValueName.Length = 0;
+ NlpMakeCacheEntryName( Index, &ValueName );
+
+ //
+ // perform first query to find out how much buffer to allocate
+ //
+
+ NtStatus = NtQueryValueKey(NlpCacheHandle,
+ &ValueName,
+ KeyValueFullInformation,
+ NULL,
+ 0,
+ &RequiredSize
+ );
+
+
+ if (NT_SUCCESS(NtStatus) || (NtStatus == STATUS_BUFFER_TOO_SMALL)) {
+
+ //
+ // allocate buffer then do query again, this time receiving data
+ //
+
+ RegInfo = (PKEY_VALUE_FULL_INFORMATION)AllocateFromHeap(RequiredSize);
+ if (RegInfo == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ NtStatus = NtQueryValueKey(NlpCacheHandle,
+ &ValueName,
+ KeyValueFullInformation,
+ (PVOID)RegInfo,
+ RequiredSize,
+ &RequiredSize
+ );
+
+#if DBG
+ if (DumpCacheInfo) {
+ DbgPrint("NlpReadCacheEntryByIndex: Index : %d\n"
+ " NtQueryValueKey returns: %d bytes\n"
+ " DataOffset=%d\n"
+ " DataLength=%d\n",
+ Index, RequiredSize, RegInfo->DataOffset, RegInfo->DataLength);
+ }
+#endif
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RCacheEntry = (PLOGON_CACHE_ENTRY)((PCHAR)RegInfo + RegInfo->DataOffset);
+ *EntrySize = RegInfo->DataLength;
+
+ (*CacheEntry) = (PLOGON_CACHE_ENTRY)AllocateFromHeap( (*EntrySize) );
+ if ((*CacheEntry) == NULL) {
+ NtStatus = STATUS_NO_MEMORY;
+ } else {
+ RtlMoveMemory( (*CacheEntry),
+ RCacheEntry,
+ (*EntrySize) );
+
+ }
+
+ FreeToHeap(RegInfo);
+
+#if DBG
+ if (DumpCacheInfo) {
+ DumpCacheEntry(Index, *CacheEntry);
+ }
+#endif
+
+ }
+
+ }
+ return(NtStatus);
+}
+
+
+VOID
+NlpAddEntryToActiveList(
+ IN ULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ Place a CTE entry in the active CTE list.
+ This requires placing the entry in the right location in
+ the list chronologically. The beginning of the list is
+ the most recently updated (or referenced) cache entry.
+ The end of the list is the oldest active cache entry.
+
+
+ Note - The entry may be already in the active list (but
+ in the wrong place), or may be on the inactive list.
+ It will be removed from whichever list it is on.
+
+Arguments:
+
+ Index - CTE table index of the entry to make active..
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PNLP_CTE
+ Next;
+
+ //
+ // Remove the entry from its current list, and then place it
+ // in the active list.
+ //
+
+
+ RemoveEntryList( &NlpCteTable[Index].Link );
+
+
+ //
+ // Now walk the active list until we find a place to insert
+ // the entry. It must follow all entries with more recent
+ // time stamps.
+ //
+
+ Next = (PNLP_CTE)NlpActiveCtes.Flink;
+
+ while (Next != (PNLP_CTE)&NlpActiveCtes) {
+
+ if ( NlpCteTable[Index].Time.QuadPart < Next->Time.QuadPart ) {
+
+ //
+ // More recent than this entry - add it here
+ //
+
+ break; // out of while-loop
+
+ }
+
+ Next = (PNLP_CTE)(Next->Link.Flink); // Advance to next entry
+ }
+
+
+ //
+ // Use the preceding entry as the list head.
+ //
+
+ InsertHeadList( Next->Link.Flink, &NlpCteTable[Index].Link );
+
+ //
+ // Mark the entry as valid
+ //
+
+ NlpCteTable[Index].Active = TRUE;
+
+ return;
+}
+
+
+VOID
+NlpAddEntryToInactiveList(
+ IN ULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ Move the CTE entry to the inactive list.
+
+ It doesn't matter if the entry is already inactive.
+
+Arguments:
+
+ Index - CTE table index of the entry to make inactive.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Remove the entry from its current list, and then place it
+ // in the inactive list.
+ //
+
+
+ RemoveEntryList( &NlpCteTable[Index].Link );
+ InsertTailList( &NlpInactiveCtes, &NlpCteTable[Index].Link );
+
+
+ //
+ // Mark the entry as invalid
+ //
+
+ NlpCteTable[Index].Active = FALSE;
+
+ return;
+}
+
+
+VOID
+NlpGetFreeEntryIndex(
+ OUT PULONG Index
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the index of either a free entry,
+ or, lacking any free entries, the oldest active entry.
+
+ The entry is left on the list it is already on. If it
+ is used by the caller, then the caller must ensure it is
+ re-assigned to the active list (using NlpAddEntryToActiveList()).
+
+ This routine is only callable if the cache is enabled (that is,
+ NlpCacheControl.Entries != 0).
+
+Arguments:
+
+ Index - Receives the index of the next available entry.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // See if the Inactive list is empty.
+ //
+
+ if (NlpInactiveCtes.Flink != &NlpInactiveCtes) {
+ (*Index) = ((PNLP_CTE)(NlpInactiveCtes.Flink))->Index;
+ } else {
+
+ //
+ // Have to return the oldest active entry.
+ //
+
+ (*Index) = ((PNLP_CTE)(NlpActiveCtes.Blink))->Index;
+ }
+
+ return;
+}
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Diagnostic support services //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// diagnostic dump routines
+//
+
+#if DBG
+
+PCHAR
+DumpOwfPasswordToString(
+ OUT PCHAR Buffer,
+ IN PLM_OWF_PASSWORD Password
+ )
+{
+ int i;
+ PCHAR bufptr;
+
+ for (i = 0, bufptr = Buffer; i < sizeof(*Password); ++i) {
+ sprintf(bufptr, "%02.2x ", ((PCHAR)Password)[i] & 0xff);
+ bufptr += 3;
+ }
+ return Buffer;
+}
+
+
+VOID
+DumpLogonInfo(
+ IN PNETLOGON_LOGON_IDENTITY_INFO LogonInfo
+ )
+{
+ CHAR ntOwfBuffer[64];
+ CHAR lmOwfBuffer[64];
+
+ DbgPrint( "\n"
+ "NETLOGON_INTERACTIVE_INFO:\n"
+ "DomainName : \"%*.*ws\"\n"
+ "UserName : \"%*.*ws\"\n"
+ "Parm Ctrl : %u (%x)\n"
+ "LogonId : %u.%u (%x.%x)\n"
+ "Workstation : \"%*.*ws\"\n",
+ LogonInfo->LogonDomainName.Length/sizeof(WCHAR),
+ LogonInfo->LogonDomainName.Length/sizeof(WCHAR),
+ LogonInfo->LogonDomainName.Buffer,
+ LogonInfo->UserName.Length/sizeof(WCHAR),
+ LogonInfo->UserName.Length/sizeof(WCHAR),
+ LogonInfo->UserName.Buffer,
+ LogonInfo->ParameterControl,
+ LogonInfo->ParameterControl,
+ LogonInfo->LogonId.HighPart,
+ LogonInfo->LogonId.LowPart,
+ LogonInfo->LogonId.HighPart,
+ LogonInfo->LogonId.LowPart,
+ LogonInfo->Workstation.Length/sizeof(WCHAR),
+ LogonInfo->Workstation.Length/sizeof(WCHAR),
+ LogonInfo->Workstation.Buffer
+ );
+}
+
+
+char*
+MapWeekday(
+ IN CSHORT Weekday
+ )
+{
+ switch (Weekday) {
+ case 0: return "Sunday";
+ case 1: return "Monday";
+ case 2: return "Tuesday";
+ case 3: return "Wednesday";
+ case 4: return "Thursday";
+ case 5: return "Friday";
+ case 6: return "Saturday";
+ }
+ return "???";
+}
+
+
+VOID
+DumpTime(
+ IN LPSTR String,
+ IN POLD_LARGE_INTEGER OldTime
+ )
+{
+ TIME_FIELDS tf;
+ LARGE_INTEGER Time;
+
+ OLD_TO_NEW_LARGE_INTEGER( (*OldTime), Time );
+
+ RtlTimeToTimeFields(&Time, &tf);
+ DbgPrint("%s%02d:%02d:%02d.%03d %02d/%02d/%d (%s [%d])\n",
+ String,
+ tf.Hour,
+ tf.Minute,
+ tf.Second,
+ tf.Milliseconds,
+ tf.Month,
+ tf.Day,
+ tf.Year,
+ MapWeekday(tf.Weekday),
+ tf.Weekday
+ );
+}
+
+
+VOID
+DumpGroupIds(
+ IN LPSTR String,
+ IN ULONG Count,
+ IN PGROUP_MEMBERSHIP GroupIds
+ )
+{
+ DbgPrint(String);
+ if (!Count) {
+ DbgPrint("No group IDs!\n");
+ } else {
+ char tab[80];
+
+ memset(tab, ' ', strlen(String));
+// tab[strcspn(String, "%")] = 0;
+ tab[strlen(String)] = 0;
+ while (Count--) {
+ DbgPrint("%d, %d\n", GroupIds->RelativeId, GroupIds->Attributes);
+ if (Count) {
+ DbgPrint(tab);
+ }
+ ++GroupIds;
+ }
+ }
+}
+
+
+VOID
+DumpSessKey(
+ IN LPSTR String,
+ IN PUSER_SESSION_KEY Key
+ )
+{
+ int len;
+ DbgPrint(String);
+ DbgPrint("%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x\n",
+ ((PUCHAR)&Key->data[0])[0],
+ ((PUCHAR)&Key->data[0])[1],
+ ((PUCHAR)&Key->data[0])[2],
+ ((PUCHAR)&Key->data[0])[3],
+ ((PUCHAR)&Key->data[0])[4],
+ ((PUCHAR)&Key->data[0])[5],
+ ((PUCHAR)&Key->data[0])[6],
+ ((PUCHAR)&Key->data[0])[7]
+ );
+ len = strlen(String);
+ DbgPrint("%-*.*s", len, len, "");
+ DbgPrint("%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x\n",
+ ((PUCHAR)&Key->data[1])[0],
+ ((PUCHAR)&Key->data[1])[1],
+ ((PUCHAR)&Key->data[1])[2],
+ ((PUCHAR)&Key->data[1])[3],
+ ((PUCHAR)&Key->data[1])[4],
+ ((PUCHAR)&Key->data[1])[5],
+ ((PUCHAR)&Key->data[1])[6],
+ ((PUCHAR)&Key->data[1])[7]
+ );
+}
+
+
+VOID
+DumpSid(
+ LPSTR String,
+ PISID Sid
+ )
+{
+ char tab[80];
+ int i;
+ PULONG psa;
+
+ DbgPrint(String);
+ memset(tab, ' ', strlen(String));
+ tab[strlen(String)] = 0;
+ DbgPrint( "Revision : %d\n"
+ "%s"
+ "SubAuthorityCount : %d\n"
+ "%s"
+ "IdentifierAuthority : %d-%d-%d-%d-%d-%d\n",
+ Sid->Revision,
+ tab,
+ Sid->SubAuthorityCount,
+ tab,
+ ((PUCHAR)&Sid->IdentifierAuthority)[0],
+ ((PUCHAR)&Sid->IdentifierAuthority)[1],
+ ((PUCHAR)&Sid->IdentifierAuthority)[2],
+ ((PUCHAR)&Sid->IdentifierAuthority)[3],
+ ((PUCHAR)&Sid->IdentifierAuthority)[4],
+ ((PUCHAR)&Sid->IdentifierAuthority)[5]
+ );
+ psa = (PULONG)&Sid->SubAuthority;
+ for (i=0; i<(int)Sid->SubAuthorityCount; ++i) {
+ DbgPrint(
+ "%s"
+ "SubAuthority : %d\n",
+ tab,
+ *psa++
+ );
+ }
+}
+
+
+VOID
+DumpAccountInfo(
+ IN PNETLOGON_VALIDATION_SAM_INFO2 AccountInfo
+ )
+{
+ DbgPrint( "\n"
+ "NETLOGON_VALIDATION_SAM_INFO:\n");
+
+ DumpTime( "LogonTime : ", &AccountInfo->LogonTime);
+
+ DumpTime( "LogoffTime : ", &AccountInfo->LogoffTime);
+
+ DumpTime( "KickOffTime : ", &AccountInfo->KickOffTime);
+
+ DumpTime( "PasswordLastSet : ", &AccountInfo->PasswordLastSet);
+
+ DumpTime( "PasswordCanChange : ", &AccountInfo->PasswordCanChange);
+
+ DumpTime( "PasswordMustChange : ", &AccountInfo->PasswordMustChange);
+
+ DbgPrint( "EffectiveName : \"%*.*ws\"\n"
+ "FullName : \"%*.*ws\"\n"
+ "LogonScript : \"%*.*ws\"\n"
+ "ProfilePath : \"%*.*ws\"\n"
+ "HomeDirectory : \"%*.*ws\"\n"
+ "HomeDirectoryDrive : \"%*.*ws\"\n"
+ "LogonCount : %d\n"
+ "BadPasswordCount : %d\n"
+ "UserId : %d\n"
+ "PrimaryGroupId : %d\n"
+ "GroupCount : %d\n",
+ AccountInfo->EffectiveName.Length/sizeof(WCHAR),
+ AccountInfo->EffectiveName.Length/sizeof(WCHAR),
+ AccountInfo->EffectiveName.Buffer,
+ AccountInfo->FullName.Length/sizeof(WCHAR),
+ AccountInfo->FullName.Length/sizeof(WCHAR),
+ AccountInfo->FullName.Buffer,
+ AccountInfo->LogonScript.Length/sizeof(WCHAR),
+ AccountInfo->LogonScript.Length/sizeof(WCHAR),
+ AccountInfo->LogonScript.Buffer,
+ AccountInfo->ProfilePath.Length/sizeof(WCHAR),
+ AccountInfo->ProfilePath.Length/sizeof(WCHAR),
+ AccountInfo->ProfilePath.Buffer,
+ AccountInfo->HomeDirectory.Length/sizeof(WCHAR),
+ AccountInfo->HomeDirectory.Length/sizeof(WCHAR),
+ AccountInfo->HomeDirectory.Buffer,
+ AccountInfo->HomeDirectoryDrive.Length/sizeof(WCHAR),
+ AccountInfo->HomeDirectoryDrive.Length/sizeof(WCHAR),
+ AccountInfo->HomeDirectoryDrive.Buffer,
+ AccountInfo->LogonCount,
+ AccountInfo->BadPasswordCount,
+ AccountInfo->UserId,
+ AccountInfo->PrimaryGroupId,
+ AccountInfo->GroupCount
+ );
+
+ DumpGroupIds("GroupIds : ",
+ AccountInfo->GroupCount,
+ AccountInfo->GroupIds
+ );
+
+ DbgPrint( "UserFlags : 0x%08x\n",
+ AccountInfo->UserFlags
+ );
+
+ DumpSessKey("UserSessionKey : ", &AccountInfo->UserSessionKey);
+
+ DbgPrint( "LogonServer : \"%*.*ws\"\n"
+ "LogonDomainName : \"%*.*ws\"\n",
+ AccountInfo->LogonServer.Length/sizeof(WCHAR),
+ AccountInfo->LogonServer.Length/sizeof(WCHAR),
+ AccountInfo->LogonServer.Buffer,
+ AccountInfo->LogonDomainName.Length/sizeof(WCHAR),
+ AccountInfo->LogonDomainName.Length/sizeof(WCHAR),
+ AccountInfo->LogonDomainName.Buffer
+ );
+
+ DumpSid( "LogonDomainId : ", (PISID)AccountInfo->LogonDomainId);
+}
+
+
+VOID
+DumpCacheEntry(
+ IN ULONG Index,
+ IN PLOGON_CACHE_ENTRY pEntry
+ )
+{
+ PUCHAR dataptr;
+ ULONG length;
+
+ DbgPrint( "\n"
+ "LOGON_CACHE_ENTRY:\n"
+ "CTE Index : %d\n", Index);
+
+ if (pEntry->Valid != TRUE) {
+ DbgPrint( "State : INVALID\n");
+ return;
+ }
+
+ dataptr = (PUCHAR)(pEntry+1);
+
+ length = pEntry->UserNameLength;
+
+ DbgPrint( "State : VALID\n");
+ DbgPrint( "UserName : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->DomainNameLength;
+ DbgPrint( "DomainName : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->EffectiveNameLength;
+ DbgPrint( "EffectiveName : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->FullNameLength;
+ DbgPrint( "FullName : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->LogonScriptLength;
+ DbgPrint( "LogonScript : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->ProfilePathLength;
+ DbgPrint( "ProfilePath : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->HomeDirectoryLength;
+ DbgPrint( "HomeDirectory : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ length = pEntry->HomeDirectoryDriveLength;
+ DbgPrint( "HomeDirectoryDrive : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+ DbgPrint( "UserId : %d\n"
+ "PrimaryGroupId : %d\n"
+ "GroupCount : %d\n",
+ pEntry->UserId,
+ pEntry->PrimaryGroupId,
+ pEntry->GroupCount
+ );
+
+ DumpGroupIds(
+ "GroupIds : ",
+ pEntry->GroupCount,
+ (PGROUP_MEMBERSHIP)dataptr
+ );
+
+ dataptr = ROUND_UP_POINTER((dataptr+pEntry->GroupCount * sizeof(GROUP_MEMBERSHIP)), sizeof(ULONG));
+
+ length = pEntry->LogonDomainNameLength;
+ DbgPrint( "LogonDomainName : \"%*.*ws\"\n", length/2, length/2, dataptr);
+ dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
+
+
+ if (pEntry->SidCount) {
+ ULONG i, sidLength;
+ PULONG SidAttributes = (PULONG) dataptr;
+
+ dataptr = ROUND_UP_POINTER(dataptr + pEntry->SidCount * sizeof(ULONG), sizeof(ULONG));
+ for (i = 0; i < pEntry->SidCount ; i++ ) {
+ sidLength = RtlLengthSid ((PSID) dataptr);
+ DumpSid("Sid : ",(PISID) dataptr);
+ DbgPrint("\tAttributes = 0x%x\n",SidAttributes[i]);
+ dataptr = ROUND_UP_POINTER(dataptr + sidLength, sizeof(ULONG));
+ }
+
+ }
+
+ DumpSid( "LogonDomainId : ", (PISID)dataptr);
+}
+#endif
diff --git a/private/lsa/msv1_0/nlpcache.h b/private/lsa/msv1_0/nlpcache.h
new file mode 100644
index 000000000..6c5678ef5
--- /dev/null
+++ b/private/lsa/msv1_0/nlpcache.h
@@ -0,0 +1,184 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nlpcache.h
+
+Abstract:
+
+ Structures and prototypes for nlpcache.c
+
+Author:
+
+ Richard L Firth (rfirth) 17-Mar-1992
+
+Revision History:
+
+--*/
+
+#define CACHE_NAME L"\\Registry\\Machine\\Security\\Cache"
+#define CACHE_NAME_SIZE (sizeof(CACHE_NAME) - sizeof(L""))
+#define CACHE_TITLE_INDEX 100 // ?
+
+//
+// current hack registry can only handles names valid on underlying file
+// system. Therefore can't have long names if registry on FAT - use LSD
+// or, alternatively, LCD
+//
+//#define SECRET_NAME L"LogonCachePassword"
+#define SECRET_NAME L"LogonPwd"
+#define SECRET_NAME_SIZE (sizeof(SECRET_NAME) - sizeof(L""))
+
+//
+// LOGON_CACHE_ENTRY - this is what we store in the cache. We don't need to
+// cache all the fields from the NETLOGON_VALIDATION_SAM_INFO - just the ones
+// we can't easily invent.
+//
+// There is additional data following the end of the structure: There are
+// <GroupCount> GROUP_MEMBERSHIP structures, followed by a SID which is the
+// LogonDomainId. The rest of the data in the entry is the buffer areas for
+// the UNICODE_STRING fields
+//
+// This structure is a strict superset of the previous version, 1_0A
+//
+
+
+typedef struct _LOGON_CACHE_ENTRY {
+ USHORT UserNameLength;
+ USHORT DomainNameLength;
+ USHORT EffectiveNameLength;
+ USHORT FullNameLength;
+
+ USHORT LogonScriptLength;
+ USHORT ProfilePathLength;
+ USHORT HomeDirectoryLength;
+ USHORT HomeDirectoryDriveLength;
+
+ ULONG UserId;
+ ULONG PrimaryGroupId;
+ ULONG GroupCount;
+ USHORT LogonDomainNameLength;
+
+ //
+ // The following fields are present in NT1.0A release and later
+ // systems.
+ //
+
+ USHORT LogonDomainIdLength; // was Unused1
+ LARGE_INTEGER Time;
+ ULONG Revision;
+ ULONG SidCount; // was Unused2
+ BOOLEAN Valid;
+ CHAR Unused[3];
+ ULONG SidLength;
+ ULONG Unused1; // unused - for later expansion
+ ULONG Unused2; // unused - for later expansion
+
+} LOGON_CACHE_ENTRY, *PLOGON_CACHE_ENTRY;
+
+//
+// This is the structure used in Daytona through build 622 or so, and
+// is preserved for backwards compatibility.
+//
+
+typedef struct _LOGON_CACHE_ENTRY_1_0A {
+ USHORT UserNameLength;
+ USHORT DomainNameLength;
+ USHORT EffectiveNameLength;
+ USHORT FullNameLength;
+
+ USHORT LogonScriptLength;
+ USHORT ProfilePathLength;
+ USHORT HomeDirectoryLength;
+ USHORT HomeDirectoryDriveLength;
+
+ ULONG UserId;
+ ULONG PrimaryGroupId;
+ ULONG GroupCount;
+ USHORT LogonDomainNameLength;
+
+ //
+ // The following fields are present in NT1.0A release and later
+ // systems.
+ //
+
+ USHORT Unused1; // Maintain QWORD alignment
+
+ LARGE_INTEGER Time;
+
+ ULONG Revision;
+ ULONG Unused2;
+
+
+ BOOLEAN Valid;
+
+} LOGON_CACHE_ENTRY_1_0A, *PLOGON_CACHE_ENTRY_1_0A;
+
+typedef struct _LOGON_CACHE_ENTRY_1_0 {
+ USHORT UserNameLength;
+ USHORT DomainNameLength;
+ USHORT EffectiveNameLength;
+ USHORT FullNameLength;
+
+ USHORT LogonScriptLength;
+ USHORT ProfilePathLength;
+ USHORT HomeDirectoryLength;
+ USHORT HomeDirectoryDriveLength;
+
+ ULONG UserId;
+ ULONG PrimaryGroupId;
+ ULONG GroupCount;
+ USHORT LogonDomainNameLength;
+} LOGON_CACHE_ENTRY_1_0, *PLOGON_CACHE_ENTRY_1_0;
+
+//
+// CACHE_PASSWORDS - passwords are stored (in secret storage) as two encrypted
+// one way function (OWF) passwords concatenated together. They must be fixed
+// length
+//
+
+typedef struct _CACHE_PASSWORDS {
+ USER_INTERNAL1_INFORMATION SecretPasswords;
+} CACHE_PASSWORDS, *PCACHE_PASSWORDS;
+
+//
+// net logon cache prototypes
+//
+
+NTSTATUS
+NlpCacheInitialize(
+ VOID
+ );
+
+NTSTATUS
+NlpCacheTerminate(
+ VOID
+ );
+
+NTSTATUS
+NlpAddCacheEntry(
+ IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
+ IN PNETLOGON_VALIDATION_SAM_INFO2 AccountInfo
+ );
+
+NTSTATUS
+NlpGetCacheEntry(
+ IN PNETLOGON_LOGON_IDENTITY_INFO LogonInfo,
+ OUT PNETLOGON_VALIDATION_SAM_INFO2* AccountInfo,
+ OUT PCACHE_PASSWORDS Passwords
+ );
+
+NTSTATUS
+NlpDeleteCacheEntry(
+ IN PNETLOGON_INTERACTIVE_INFO LogonInfo
+ );
+
+VOID
+NlpChangeCachePassword(
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING UserName,
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PNT_OWF_PASSWORD NtOwfPassword
+ );
diff --git a/private/lsa/msv1_0/nlvars.c b/private/lsa/msv1_0/nlvars.c
new file mode 100644
index 000000000..8a3ec73d5
--- /dev/null
+++ b/private/lsa/msv1_0/nlvars.c
@@ -0,0 +1,86 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nlvars.c
+
+Abstract:
+
+ This module contains variables used within the msv1_0 authentication
+ package.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 29-Apr-1991
+
+Environment:
+
+ User mode - msv1_0 authentication package DLL
+
+Revision History:
+
+
+--*/
+
+#include "msp.h"
+#include "nlp.h"
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// READ ONLY Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+//
+// Null copies of Lanman and NT OWF password.
+//
+
+LM_OWF_PASSWORD NlpNullLmOwfPassword;
+NT_OWF_PASSWORD NlpNullNtOwfPassword;
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// READ/WRITE Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+//
+// Define the list of active interactive logons.
+//
+// The NlpActiveLogonLock must be locked while referencing the list or
+// any of its elements.
+//
+
+RTL_CRITICAL_SECTION NlpActiveLogonLock;
+PACTIVE_LOGON NlpActiveLogons;
+
+//
+// Define the running enumeration handle.
+//
+// This variable defines the enumeration handle to assign to a logon
+// session. It will be incremented prior to assigning it value to
+// the next created logon session. Access is serialize using
+// NlpActiveLogonLocks.
+
+ULONG NlpEnumerationHandle;
+
+//
+// Define a running Session Number which is incremented once for each
+// challenge given to the server.
+//
+
+RTL_CRITICAL_SECTION NlpSessionCountLock;
+ULONG NlpSessionCount;
+
+//
+// Define the number of successful/unsuccessful logons attempts.
+//
+
+ULONG NlpLogonAttemptCount;
diff --git a/private/lsa/msv1_0/pwdtest.c b/private/lsa/msv1_0/pwdtest.c
new file mode 100644
index 000000000..990166eb0
--- /dev/null
+++ b/private/lsa/msv1_0/pwdtest.c
@@ -0,0 +1,401 @@
+/*--
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ pwdtest.c
+
+Abstract:
+
+ Test program for the changing passwords.
+
+Author:
+
+ 30-Apr-1993 (cliffv)
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+
+--*/
+
+
+//
+// Common include files.
+//
+
+#include <msp.h>
+#define NLP_ALLOCATE
+#include <nlp.h>
+#include <lsarpc.h> // Lsar routines
+#include <lsaisrv.h> // LsaIFree and Trusted Client Routines
+#include <stdio.h>
+
+
+//
+// Dummy routines from LSA
+//
+
+NTSTATUS
+LsapAllocateClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG LengthRequired,
+ OUT PVOID *ClientBaseAddress
+ )
+
+{
+
+ *ClientBaseAddress = RtlAllocateHeap( MspHeap, 0, LengthRequired );
+
+ if ( *ClientBaseAddress == NULL ) {
+ return(STATUS_QUOTA_EXCEEDED);
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+LsapFreeClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ClientBaseAddress OPTIONAL
+ )
+{
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+LsapCopyToClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG Length,
+ IN PVOID ClientBaseAddress,
+ IN PVOID BufferToCopy
+ )
+
+{
+ RtlMoveMemory( ClientBaseAddress, BufferToCopy, Length );
+ return(STATUS_SUCCESS);
+}
+
+
+int _CRTAPI1
+main(
+ IN int argc,
+ IN char ** argv
+ )
+/*++
+
+Routine Description:
+
+ Drive the password changing.
+
+Arguments:
+
+ argc - the number of command-line arguments.
+
+ argv - an array of pointers to the arguments.
+
+Return Value:
+
+ Exit status
+
+--*/
+{
+ NTSTATUS Status;
+ MSV1_0_CHANGEPASSWORD_REQUEST Request;
+ PMSV1_0_CHANGEPASSWORD_RESPONSE ReturnBuffer;
+ ULONG ReturnBufferSize;
+ NTSTATUS ProtocolStatus;
+ OBJECT_ATTRIBUTES LSAObjectAttributes;
+ UNICODE_STRING LocalComputerName = { 0, 0, NULL };
+ PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo = NULL;
+
+ if ( argc < 5 ) {
+ fprintf( stderr,
+ "Usage: pwdtest DomainName UserName OldPassword NewPassword\n" );
+ return(1);
+ }
+
+ //
+ // Set up MSV1_0.dll environment.
+ //
+
+ MspHeap = RtlProcessHeap();
+
+ Status = NlInitialize();
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf("pwdtest: NlInitialize failed, status %x\n", Status);
+ return(1);
+ }
+
+ Lsa.AllocateClientBuffer = LsapAllocateClientBuffer;
+ Lsa.FreeClientBuffer = LsapFreeClientBuffer;
+ Lsa.CopyToClientBuffer = LsapCopyToClientBuffer;
+
+
+
+ //
+ // Open the LSA policy database in case change password needs it
+ //
+
+ InitializeObjectAttributes( &LSAObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ Status = LsaOpenPolicy( &LocalComputerName,
+ &LSAObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &NlpPolicyHandle );
+
+ if( !NT_SUCCESS(Status) ) {
+ printf("pwdtest: LsaOpenPolicy failed, status %x\n", Status);
+ return(1);
+ }
+
+
+ //
+ // Get the name of our domain.
+ //
+
+ Status = LsaQueryInformationPolicy(
+ NlpPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PVOID *) &PrimaryDomainInfo );
+
+ if( !NT_SUCCESS(Status) ) {
+ KdPrint(("pwdtest: LsaQueryInformationPolicy failed, status %x\n",
+ Status));
+ return(1);
+ }
+
+ NlpSamDomainName = PrimaryDomainInfo->Name;
+
+
+
+ //
+ // Build the request message
+ //
+
+ Request.MessageType = MsV1_0ChangePassword;
+ RtlCreateUnicodeStringFromAsciiz( &Request.DomainName, argv[1] );
+ RtlCreateUnicodeStringFromAsciiz( &Request.AccountName, argv[2] );
+ RtlCreateUnicodeStringFromAsciiz( &Request.OldPassword, argv[3] );
+ RtlCreateUnicodeStringFromAsciiz( &Request.NewPassword, argv[4] );
+
+ Status = MspLm20ChangePassword( NULL,
+ &Request,
+ &Request,
+ 0x7FFFFFFF,
+ (PVOID *) &ReturnBuffer,
+ &ReturnBufferSize,
+ &ProtocolStatus );
+
+ printf( "Status = 0x%lx 0x%lx\n", Status, ProtocolStatus );
+
+ if ( ProtocolStatus == STATUS_CANT_DISABLE_MANDATORY ) {
+ printf( "Are you running as SYSTEM?\n" );
+ }
+
+ if ( ReturnBufferSize != 0 ) {
+ printf( "PasswordInfoValid %ld\n", ReturnBuffer->PasswordInfoValid );
+ if ( ReturnBuffer->PasswordInfoValid ) {
+ printf( "Min length: %ld PasswordHistory: %ld Prop 0x%lx\n",
+ ReturnBuffer->DomainPasswordInfo.MinPasswordLength,
+ ReturnBuffer->DomainPasswordInfo.PasswordHistoryLength,
+ ReturnBuffer->DomainPasswordInfo.PasswordProperties );
+ }
+ }
+ return 0;
+
+
+}
+
+
+//
+// Stub routines needed by msvpaswd.c
+//
+
+NTSTATUS
+LsarQueryInformationPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InfoClass,
+ OUT PLSAPR_POLICY_INFORMATION *Buffer
+ )
+{
+ return( LsaQueryInformationPolicy( PolicyHandle,
+ InfoClass,
+ Buffer ) );
+}
+
+VOID
+LsaIFree_LSAPR_POLICY_INFORMATION (
+ POLICY_INFORMATION_CLASS InfoClass,
+ PLSAPR_POLICY_INFORMATION Buffer
+ )
+{
+}
+
+NTSTATUS
+NlpChangePassword(
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING UserName,
+ IN PLM_OWF_PASSWORD LmOwfPassword,
+ IN PNT_OWF_PASSWORD NtOwfPassword
+ )
+{
+ return(STATUS_SUCCESS);
+}
+
+
+
+NTSTATUS
+NlInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize NETLOGON portion of msv1_0 authentication package.
+
+Arguments:
+
+ None.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LPWSTR ComputerName;
+ DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
+ NT_PRODUCT_TYPE NtProductType;
+ UNICODE_STRING TempUnicodeString;
+
+ //
+ // Initialize global data
+ //
+
+ NlpEnumerationHandle = 0;
+ NlpSessionCount = 0;
+
+ NlpComputerName.Buffer = NULL;
+ NlpSamDomainName.Buffer = NULL;
+ NlpSamDomainId = NULL;
+ NlpSamDomainHandle = NULL;
+
+
+
+ //
+ // Get the name of this machine.
+ //
+
+ ComputerName = RtlAllocateHeap(
+ MspHeap, 0,
+ ComputerNameLength * sizeof(WCHAR) );
+
+ if (ComputerName == NULL ||
+ !GetComputerNameW( ComputerName, &ComputerNameLength )) {
+
+ KdPrint(( "MsV1_0: Cannot get computername %lX\n", GetLastError() ));
+
+ NlpLanmanInstalled = FALSE;
+ RtlFreeHeap( MspHeap, 0, ComputerName );
+ ComputerName = NULL;
+ } else {
+
+ NlpLanmanInstalled = TRUE;
+ }
+
+ RtlInitUnicodeString( &NlpComputerName, ComputerName );
+
+ //
+ // Determine if this machine is running Windows NT or Lanman NT.
+ // LanMan NT runs on a domain controller.
+ //
+
+ if ( !RtlGetNtProductType( &NtProductType ) ) {
+ KdPrint(( "MsV1_0: Nt Product Type undefined (WinNt assumed)\n" ));
+ NtProductType = NtProductWinNt;
+ }
+
+ NlpWorkstation = (BOOLEAN)(NtProductType != NtProductLanManNt);
+
+
+#ifdef notdef
+
+ //
+ // Initialize any locks.
+ //
+
+ RtlInitializeCriticalSection(&NlpActiveLogonLock);
+ RtlInitializeCriticalSection(&NlpSessionCountLock);
+
+ //
+ // initialize the cache - creates a critical section is all
+ //
+
+ NlpCacheInitialize();
+#endif // notdef
+
+
+ //
+ // Attempt to load Netapi.dll
+ //
+
+ NlpLoadNetapiDll();
+
+#ifdef COMPILED_BY_DEVELOPER
+ KdPrint(("msv1_0: COMPILED_BY_DEVELOPER breakpoint.\n"));
+ DbgBreakPoint();
+#endif // COMPILED_BY_DEVELOPER
+
+
+
+ //
+ // Initialize useful encryption constants
+ //
+
+ Status = RtlCalculateLmOwfPassword( "", &NlpNullLmOwfPassword );
+ ASSERT( NT_SUCCESS(Status) );
+
+ RtlInitUnicodeString(&TempUnicodeString, NULL);
+ Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
+ &NlpNullNtOwfPassword);
+ ASSERT( NT_SUCCESS(Status) );
+
+
+
+
+#ifdef notdef
+ //
+ // If we weren't successful,
+ // Clean up global resources we intended to initialize.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ if ( NlpComputerName.Buffer != NULL ) {
+ MIDL_user_free( NlpComputerName.Buffer );
+ }
+
+ }
+#endif // notdef
+
+ return STATUS_SUCCESS;
+
+}
diff --git a/private/lsa/msv1_0/sources b/private/lsa/msv1_0/sources
new file mode 100644
index 000000000..ca3fc6335
--- /dev/null
+++ b/private/lsa/msv1_0/sources
@@ -0,0 +1,76 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=msv1_0
+
+TARGETNAME=msv1_0
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+
+TARGETLIBS= $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\lsasrv.lib
+
+TARGETTYPE=DYNLINK
+
+#
+# Define COMPILED_BY_DEVELOPER to hit a breakpoint during initialization.
+#
+#C_DEFINES= -DCOMPILED_BY_DEVELOPER -DDUMP_CACHE_INFO
+
+C_DEFINES=-DUNICODE=1 -DRPC_NO_WINDOWS_H
+
+
+DLLBASE=@$(BASEDIR)\PUBLIC\SDK\LIB\coffbase.txt,msv1_0
+
+
+INCLUDES=.;..\inc;..\..\inc;..\..\net\inc
+
+NTPROFILEINPUT=yes
+MSC_WARNING_LEVEL=/W3 /WX
+
+USE_NTDLL=1
+
+SOURCES= \
+ msv1_0.c \
+ msv1_0.rc \
+ msvars.c \
+ msvpaswd.c \
+ msvsam.c \
+ nlmain.c \
+ nlnetapi.c \
+ nlp.c \
+ nlpcache.c \
+ subauth.c
+
+
+UMTYPE=console
+UMTEST=pwdtest
+UMLIBS=obj\*\msvpaswd.obj \
+ obj\*\nlnetapi.obj \
+ obj\*\msvars.obj \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib
diff --git a/private/lsa/msv1_0/subauth.c b/private/lsa/msv1_0/subauth.c
new file mode 100644
index 000000000..937056451
--- /dev/null
+++ b/private/lsa/msv1_0/subauth.c
@@ -0,0 +1,447 @@
+/*++
+
+Copyright (c) 1987-1994 Microsoft Corporation
+
+Module Name:
+
+ subauth.c
+
+Abstract:
+
+ Interface to SubAuthentication Package.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 23-May-1994
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#include "msp.h"
+#include "nlp.h"
+#include <winreg.h>
+
+//
+// Prototype for subauthentication routine.
+//
+typedef NTSTATUS
+(*PSUBAUTHENTICATION_ROUTINE)(
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN ULONG Flags,
+ IN PUSER_ALL_INFORMATION UserAll,
+ OUT PULONG WhichFields,
+ OUT PULONG UserFlags,
+ OUT PBOOLEAN Authoritative,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime
+);
+
+//
+// Structure describing a loaded SubAuthentication DLL.
+//
+
+typedef struct _SUBAUTHENTICATION_DLL {
+ LIST_ENTRY Next;
+ ULONG DllNumber;
+ PSUBAUTHENTICATION_ROUTINE SubAuthenticationRoutine;
+} SUBAUTHENTICATION_DLL, *PSUBAUTHENTICATION_DLL;
+
+//
+// Global list of all loaded subauthentication DLLs
+//
+
+LIST_ENTRY SubAuthenticationDlls;
+CRITICAL_SECTION SubAuthenticationCritSect;
+
+
+VOID
+Msv1_0SubAuthenticationInitialization(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ Initialization routine for this source file.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ InitializeCriticalSection( &SubAuthenticationCritSect );
+ InitializeListHead( &SubAuthenticationDlls );
+}
+
+
+NTSTATUS
+Msv1_0SubAuthenticationRoutine(
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN ULONG Flags,
+ IN PUSER_ALL_INFORMATION UserAll,
+ OUT PULONG WhichFields,
+ OUT PULONG UserFlags,
+ OUT PBOOLEAN Authoritative,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime
+)
+/*++
+
+Routine Description:
+
+ The subauthentication routine does client/server specific authentication
+ of a user. This stub routine loads the appropriate subauthentication
+ package DLL and calls out to that DLL to do the actuall validation.
+
+Arguments:
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on. The LogonDomainName field should be ignored.
+
+ Flags - Flags describing the circumstances of the logon.
+
+ MSV1_0_PASSTHRU -- This is a PassThru authenication. (i.e., the
+ user isn't connecting to this machine.)
+ MSV1_0_GUEST_LOGON -- This is a retry of the logon using the GUEST
+ user account.
+
+ UserAll -- The description of the user as returned from SAM.
+
+ WhichFields -- Returns which fields from UserAllInfo are to be written
+ back to SAM. The fields will only be written if MSV returns success
+ to it's caller. Only the following bits are valid.
+
+ USER_ALL_PARAMETERS - Write UserAllInfo->Parameters back to SAM. If
+ the size of the buffer is changed, Msv1_0SubAuthenticationRoutine
+ must delete the old buffer using MIDL_user_free() and reallocate the
+ buffer using MIDL_user_allocate().
+
+ UserFlags -- Returns UserFlags to be returned from LsaLogonUser in the
+ LogonProfile. The following bits are currently defined:
+
+
+ LOGON_GUEST -- This was a guest logon
+ LOGON_NOENCRYPTION -- The caller didn't specify encrypted credentials
+ LOGON_GRACE_LOGON -- The caller's password has expired but logon
+ was allowed during a grace period following the expiration.
+
+ SubAuthentication packages should restrict themselves to returning
+ bits in the high order byte of UserFlags. However, this convention
+ isn't enforced giving the SubAuthentication package more flexibility.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+ LogoffTime - Receives the time at which the user should logoff the
+ system. This time is specified as a GMT relative NT system time.
+
+ KickoffTime - Receives the time at which the user should be kicked
+ off the system. This time is specified as a GMT relative NT system
+ time. Specify, a full scale positive number if the user isn't to
+ be kicked off.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+
+ STATUS_NO_SUCH_USER: The specified user has no account.
+ STATUS_WRONG_PASSWORD: The password was invalid.
+
+ STATUS_INVALID_INFO_CLASS: LogonLevel is invalid.
+ STATUS_ACCOUNT_LOCKED_OUT: The account is locked out
+ STATUS_ACCOUNT_DISABLED: The account is disabled
+ STATUS_ACCOUNT_EXPIRED: The account has expired.
+ STATUS_PASSWORD_MUST_CHANGE: Account is marked as Password must change
+ on next logon.
+ STATUS_PASSWORD_EXPIRED: The Password is expired.
+ STATUS_INVALID_LOGON_HOURS - The user is not authorized to logon at
+ this time.
+ STATUS_INVALID_WORKSTATION - The user is not authorized to logon to
+ the specified workstation.
+
+--*/
+{
+ NTSTATUS Status;
+ LONG RegStatus;
+
+ ULONG DllNumber;
+ PSUBAUTHENTICATION_DLL SubAuthenticationDll = NULL;
+ PSUBAUTHENTICATION_ROUTINE SubAuthenticationRoutine = NULL;
+
+ PNETLOGON_LOGON_IDENTITY_INFO LogonInfo;
+ BOOLEAN CritSectLocked = FALSE;
+
+ HKEY BaseHandle = NULL;
+ HKEY ParmHandle = NULL;
+
+ CHAR ValueName[sizeof(MSV1_0_SUBAUTHENTICATION_VALUE)+3];
+ CHAR DllName[MAXIMUM_FILENAME_LENGTH+1];
+ DWORD DllNameSize;
+ DWORD DllNameType;
+ HINSTANCE DllHandle = NULL;
+
+ PLIST_ENTRY ListEntry;
+
+
+ //
+ // Initialization
+ //
+
+ LogonInfo = (PNETLOGON_LOGON_IDENTITY_INFO) LogonInformation;
+
+ DllNumber = LogonInfo->ParameterControl >> MSV1_0_SUBAUTHENTICATION_DLL_SHIFT;
+ *Authoritative = TRUE;
+
+ EnterCriticalSection( &SubAuthenticationCritSect );
+ CritSectLocked = TRUE;
+
+ //
+ // See if the SubAuthentication Dll is already loaded.
+ //
+
+ SubAuthenticationDll = NULL;
+ for ( ListEntry = SubAuthenticationDlls.Flink ;
+ ListEntry != &SubAuthenticationDlls ;
+ ListEntry = ListEntry->Flink) {
+
+ SubAuthenticationDll = CONTAINING_RECORD( ListEntry,
+ SUBAUTHENTICATION_DLL,
+ Next );
+
+ if ( SubAuthenticationDll->DllNumber == DllNumber ) {
+ break;
+ }
+
+ SubAuthenticationDll = NULL;
+
+ }
+
+ //
+ // If the Dll is not already loaded,
+ // load it.
+ //
+
+ if ( SubAuthenticationDll == NULL ) {
+
+
+ //
+ // Open the registry
+ //
+
+ RegStatus = RegConnectRegistryW( NULL,
+ HKEY_LOCAL_MACHINE,
+ &BaseHandle);
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ KdPrint(( "MSV1_0: Cannot connect to registy %ld.\n", RegStatus ));
+ Status = STATUS_DLL_NOT_FOUND;
+ goto Cleanup;
+ }
+
+
+ //
+ // Open the MSV1_0 registry key.
+ //
+
+ RegStatus = RegOpenKeyExA(
+ BaseHandle,
+ MSV1_0_SUBAUTHENTICATION_KEY,
+ 0, //Reserved
+ KEY_QUERY_VALUE,
+ &ParmHandle );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ KdPrint(( "MSV1_0: Cannot open registry key %s %ld.\n",
+ MSV1_0_SUBAUTHENTICATION_KEY,
+ RegStatus ));
+ Status = STATUS_DLL_NOT_FOUND;
+ goto Cleanup;
+ }
+
+
+ //
+ // Build the name of the registry value.
+ //
+
+ RtlCopyMemory( ValueName,
+ MSV1_0_SUBAUTHENTICATION_VALUE,
+ sizeof(MSV1_0_SUBAUTHENTICATION_VALUE) );
+
+ Status = RtlIntegerToChar(
+ DllNumber,
+ 10, // Base
+ 4, // Length of buffer
+ &ValueName[sizeof(MSV1_0_SUBAUTHENTICATION_VALUE)-1] );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Get the registry value.
+ //
+
+ DllNameSize = sizeof(DllName);
+
+ RegStatus = RegQueryValueExA(
+ ParmHandle,
+ ValueName,
+ NULL, // Reserved
+ &DllNameType,
+ DllName,
+ &DllNameSize );
+
+ if ( RegStatus != ERROR_SUCCESS ) {
+ KdPrint(( "MSV1_0: Cannot query registry value %s %ld.\n",
+ ValueName,
+ RegStatus ));
+ Status = STATUS_DLL_NOT_FOUND;
+ goto Cleanup;
+ }
+
+ if ( DllNameType != REG_SZ ) {
+ KdPrint(( "MSV1_0: Registry value %s isn't REG_SZ.\n",
+ ValueName ));
+ Status = STATUS_DLL_NOT_FOUND;
+ goto Cleanup;
+ }
+
+ //
+ // Load the DLL
+ //
+
+ DllHandle = LoadLibraryA( DllName );
+
+ if ( DllHandle == NULL ) {
+ KdPrint(( "MSV1_0: Cannot load dll %s %ld.\n",
+ DllName,
+ GetLastError() ));
+ Status = STATUS_DLL_NOT_FOUND;
+ goto Cleanup;
+ }
+
+ //
+ // Find the SubAuthenticationRoutine. For packages other than
+ // zero, this will be Msv1_0SubauthenticationRoutine. For packge
+ // zero it will be Msv1_0SubauthenticationFilter.
+ //
+
+ if (DllNumber == 0) {
+ SubAuthenticationRoutine = (PSUBAUTHENTICATION_ROUTINE)
+ GetProcAddress(DllHandle, "Msv1_0SubAuthenticationFilter");
+
+ } else {
+ SubAuthenticationRoutine = (PSUBAUTHENTICATION_ROUTINE)
+ GetProcAddress(DllHandle, "Msv1_0SubAuthenticationRoutine");
+
+ }
+
+ if ( SubAuthenticationRoutine == NULL ) {
+ KdPrint(( "MSV1_0: Cannot find required entry point in %s %ld.\n",
+ DllName,
+ GetLastError() ));
+ Status = STATUS_PROCEDURE_NOT_FOUND;
+ goto Cleanup;
+ }
+
+
+ //
+ // Cache the address of the procedure.
+ //
+
+ SubAuthenticationDll =
+ RtlAllocateHeap(MspHeap, 0, sizeof(SUBAUTHENTICATION_DLL));
+
+ if ( SubAuthenticationDll == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ SubAuthenticationDll->DllNumber = DllNumber;
+ SubAuthenticationDll->SubAuthenticationRoutine = SubAuthenticationRoutine;
+ InsertHeadList( &SubAuthenticationDlls, &SubAuthenticationDll->Next );
+
+ DllHandle = NULL;
+
+ }
+
+ //
+ // Leave the crit sect while calling the DLL
+ //
+
+ SubAuthenticationRoutine = SubAuthenticationDll->SubAuthenticationRoutine;
+ LeaveCriticalSection( &SubAuthenticationCritSect );
+ CritSectLocked = FALSE;
+
+
+ //
+ // Call the actual authentication routine.
+ //
+
+ Status = (*SubAuthenticationRoutine)(
+ LogonLevel,
+ LogonInformation,
+ Flags,
+ UserAll,
+ WhichFields,
+ UserFlags,
+ Authoritative,
+ LogoffTime,
+ KickoffTime );
+
+ //
+ // Cleanup up before returning.
+ //
+
+Cleanup:
+
+ //
+ // If this was package zero and we didn't find it, remember it for
+ // next time.
+ //
+
+ if (!NT_SUCCESS(Status) && (DllNumber == 0) && (SubAuthenticationDll == NULL)) {
+ NlpSubAuthZeroExists = FALSE;
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( BaseHandle != NULL ) {
+ RegCloseKey( BaseHandle );
+ }
+
+ if ( ParmHandle != NULL ) {
+ RegCloseKey( ParmHandle );
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+ if ( DllHandle != NULL ) {
+ FreeLibrary( DllHandle );
+ }
+ }
+
+ if ( CritSectLocked ) {
+ LeaveCriticalSection( &SubAuthenticationCritSect );
+ }
+
+ return Status;
+}
diff --git a/private/lsa/msv1_0/subauth/makefile b/private/lsa/msv1_0/subauth/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/msv1_0/subauth/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/msv1_0/subauth/sources b/private/lsa/msv1_0/subauth/sources
new file mode 100644
index 000000000..f02104eb3
--- /dev/null
+++ b/private/lsa/msv1_0/subauth/sources
@@ -0,0 +1,54 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=msv1_0
+COMPILER_WARNINGS=
+
+TARGETNAME=subauth
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+
+TARGETLIBS= $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib
+
+
+TARGETTYPE=DYNLINK
+
+#
+# Define COMPILED_BY_DEVELOPER to hit a breakpoint during initialization.
+#
+#C_DEFINES= -DCOMPILED_BY_DEVELOPER -DDUMP_CACHE_INFO
+
+C_DEFINES=-DUNICODE=1 -DRPC_NO_WINDOWS_H
+
+
+DLLBASE=@$(BASEDIR)\PUBLIC\SDK\LIB\coffbase.txt,msv1_0
+
+
+INCLUDES=..
+
+SOURCES= \
+ subauth.c
diff --git a/private/lsa/msv1_0/subauth/subauth.c b/private/lsa/msv1_0/subauth/subauth.c
new file mode 100644
index 000000000..642999875
--- /dev/null
+++ b/private/lsa/msv1_0/subauth/subauth.c
@@ -0,0 +1,1354 @@
+/*++
+
+Copyright (c) 1987-1994 Microsoft Corporation
+
+Module Name:
+
+ subauth.c
+
+Abstract:
+
+ Sample SubAuthentication Package.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 23-May-1994
+
+Revisions:
+
+ Andy Herron (andyhe) 21-Jun-1994 Added code to read domain/user info
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+
+#if ( _MSC_VER >= 800 )
+#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
+#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
+#endif
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#undef WIN32_NO_STATUS
+#include <windows.h>
+#include <lmcons.h>
+#include <lmaccess.h>
+#include <lmapibuf.h>
+#include <subauth.h>
+
+
+BOOLEAN
+EqualComputerName(
+ IN PUNICODE_STRING String1,
+ IN PUNICODE_STRING String2
+ );
+
+NTSTATUS
+QuerySystemTime (
+ OUT PLARGE_INTEGER SystemTime
+ );
+
+
+BOOL
+GetPasswordExpired(
+ IN LARGE_INTEGER PasswordLastSet,
+ IN LARGE_INTEGER MaxPasswordAge
+ );
+
+NTSTATUS
+AccountRestrictions(
+ IN ULONG UserRid,
+ IN PUNICODE_STRING LogonWorkStation,
+ IN PUNICODE_STRING WorkStations,
+ IN PLOGON_HOURS LogonHours,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime
+ );
+
+LARGE_INTEGER
+NetpSecondsToDeltaTime(
+ IN ULONG Seconds
+ );
+
+VOID
+InitUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PCWSTR SourceString OPTIONAL
+ );
+
+VOID
+CopyUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString OPTIONAL
+ );
+
+
+
+NTSTATUS
+Msv1_0SubAuthenticationRoutine (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN ULONG Flags,
+ IN PUSER_ALL_INFORMATION UserAll,
+ OUT PULONG WhichFields,
+ OUT PULONG UserFlags,
+ OUT PBOOLEAN Authoritative,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime
+)
+/*++
+
+Routine Description:
+
+ The subauthentication routine does cient/server specific authentication
+ of a user. The credentials of the user are passed in addition to all the
+ information from SAM defining the user. This routine decides whether to
+ let the user logon.
+
+
+Arguments:
+
+ LogonLevel -- Specifies the level of information given in
+ LogonInformation.
+
+ LogonInformation -- Specifies the description for the user
+ logging on. The LogonDomainName field should be ignored.
+
+ Flags - Flags describing the circumstances of the logon.
+
+ MSV1_0_PASSTHRU -- This is a PassThru authenication. (i.e., the
+ user isn't connecting to this machine.)
+ MSV1_0_GUEST_LOGON -- This is a retry of the logon using the GUEST
+ user account.
+
+ UserAll -- The description of the user as returned from SAM.
+
+ WhichFields -- Returns which fields from UserAllInfo are to be written
+ back to SAM. The fields will only be written if MSV returns success
+ to it's caller. Only the following bits are valid.
+
+ USER_ALL_PARAMETERS - Write UserAllInfo->Parameters back to SAM. If
+ the size of the buffer is changed, Msv1_0SubAuthenticationRoutine
+ must delete the old buffer using MIDL_user_free() and reallocate the
+ buffer using MIDL_user_allocate().
+
+ UserFlags -- Returns UserFlags to be returned from LsaLogonUser in the
+ LogonProfile. The following bits are currently defined:
+
+
+ LOGON_GUEST -- This was a guest logon
+ LOGON_NOENCRYPTION -- The caller didn't specify encrypted credentials
+
+ SubAuthentication packages should restrict themselves to returning
+ bits in the high order byte of UserFlags. However, this convention
+ isn't enforced giving the SubAuthentication package more flexibility.
+
+ Authoritative -- Returns whether the status returned is an
+ authoritative status which should be returned to the original
+ caller. If not, this logon request may be tried again on another
+ domain controller. This parameter is returned regardless of the
+ status code.
+
+ LogoffTime - Receives the time at which the user should logoff the
+ system. This time is specified as a GMT relative NT system time.
+
+ KickoffTime - Receives the time at which the user should be kicked
+ off the system. This time is specified as a GMT relative NT system
+ time. Specify, a full scale positive number if the user isn't to
+ be kicked off.
+
+Return Value:
+
+ STATUS_SUCCESS: if there was no error.
+
+ STATUS_NO_SUCH_USER: The specified user has no account.
+ STATUS_WRONG_PASSWORD: The password was invalid.
+
+ STATUS_INVALID_INFO_CLASS: LogonLevel is invalid.
+ STATUS_ACCOUNT_LOCKED_OUT: The account is locked out
+ STATUS_ACCOUNT_DISABLED: The account is disabled
+ STATUS_ACCOUNT_EXPIRED: The account has expired.
+ STATUS_PASSWORD_MUST_CHANGE: Account is marked as Password must change
+ on next logon.
+ STATUS_PASSWORD_EXPIRED: The Password is expired.
+ STATUS_INVALID_LOGON_HOURS - The user is not authorized to logon at
+ this time.
+ STATUS_INVALID_WORKSTATION - The user is not authorized to logon to
+ the specified workstation.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG UserAccountControl;
+ LARGE_INTEGER LogonTime;
+ LARGE_INTEGER PasswordDateSet;
+ UNICODE_STRING LocalWorkstation;
+
+ PNETLOGON_NETWORK_INFO LogonNetworkInfo;
+
+
+ //
+ // Check whether the SubAuthentication package supports this type
+ // of logon.
+ //
+
+ *Authoritative = TRUE;
+ *UserFlags = 0;
+ *WhichFields = 0;
+
+ (VOID) QuerySystemTime( &LogonTime );
+
+ switch ( LogonLevel ) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+
+ //
+ // This SubAuthentication package only supports network logons.
+ //
+
+ return STATUS_INVALID_INFO_CLASS;
+
+ case NetlogonNetworkInformation:
+
+ //
+ // This SubAuthentication package doesn't support access via machine
+ // accounts.
+ //
+
+ UserAccountControl = USER_NORMAL_ACCOUNT;
+
+ //
+ // Local user (Temp Duplicate) accounts are only used on the machine
+ // being directly logged onto.
+ // (Nor are interactive or service logons allowed to them.)
+ //
+
+ if ( (Flags & MSV1_0_PASSTHRU) == 0 ) {
+ UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
+ }
+
+ LogonNetworkInfo = (PNETLOGON_NETWORK_INFO) LogonInformation;
+
+ break;
+
+ default:
+ *Authoritative = TRUE;
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+
+
+
+ //
+ // If the account type isn't allowed,
+ // Treat this as though the User Account doesn't exist.
+ //
+
+ if ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) {
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+ //
+ // This SubAuthentication package doesn't allow guest logons.
+ //
+ if ( Flags & MSV1_0_GUEST_LOGON ) {
+ *Authoritative = FALSE;
+ Status = STATUS_NO_SUCH_USER;
+ goto Cleanup;
+ }
+
+
+
+ //
+ // Ensure the account isn't locked out.
+ //
+
+ if ( UserAll->UserId != DOMAIN_USER_RID_ADMIN &&
+ (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) ) {
+
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ *Authoritative = FALSE;
+ } else {
+ *Authoritative = TRUE;
+ }
+ Status = STATUS_ACCOUNT_LOCKED_OUT;
+ goto Cleanup;
+ }
+
+
+ //
+ // Check the password.
+ //
+
+ if ( FALSE /* VALIDATE THE USER'S PASSWORD HERE */ ) {
+
+ Status = STATUS_WRONG_PASSWORD;
+
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ *Authoritative = FALSE;
+ } else {
+ *Authoritative = TRUE;
+ }
+
+ goto Cleanup;
+ }
+
+ //
+ // Prevent some things from effecting the Administrator user
+ //
+
+ if (UserAll->UserId == DOMAIN_USER_RID_ADMIN) {
+
+ //
+ // The administrator account doesn't have a forced logoff time.
+ //
+
+ LogoffTime->HighPart = 0x7FFFFFFF;
+ LogoffTime->LowPart = 0xFFFFFFFF;
+
+ KickoffTime->HighPart = 0x7FFFFFFF;
+ KickoffTime->LowPart = 0xFFFFFFFF;
+
+ } else {
+
+ //
+ // Check if the account is disabled.
+ //
+
+ if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
+ //
+ // Since the UI strongly encourages admins to disable user
+ // accounts rather than delete them. Treat disabled acccount as
+ // non-authoritative allowing the search to continue for other
+ // accounts by the same name.
+ //
+ *Authoritative = FALSE;
+ Status = STATUS_ACCOUNT_DISABLED;
+ goto Cleanup;
+ }
+
+ //
+ // Check if the account has expired.
+ //
+
+ if ( UserAll->AccountExpires.QuadPart != 0 &&
+ LogonTime.QuadPart >= UserAll->AccountExpires.QuadPart ) {
+ *Authoritative = TRUE;
+ Status = STATUS_ACCOUNT_EXPIRED;
+ goto Cleanup;
+ }
+
+#if 1
+
+ //
+ // If your using SAM's password expiration date, use this code, otherwise
+ // use the code below and supply your own password set date...
+ //
+
+ //
+ // The password is valid, check to see if the password is expired.
+ // (SAM will have appropriately set PasswordMustChange to reflect
+ // USER_DONT_EXPIRE_PASSWORD)
+ //
+ // If the password checked above is not the SAM password, you may
+ // want to consider not checking the SAM password expiration times here.
+ //
+
+ if ( LogonTime.QuadPart >= UserAll->PasswordMustChange.QuadPart ) {
+
+ if ( UserAll->PasswordLastSet.QuadPart == 0 ) {
+ Status = STATUS_PASSWORD_MUST_CHANGE;
+ } else {
+ Status = STATUS_PASSWORD_EXPIRED;
+ }
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+
+#else
+
+ //
+ // Response is correct. So, check if the password has expired or not
+ //
+
+ if (! (UserAll->UserAccountControl & USER_DONT_EXPIRE_PASSWORD)) {
+ LARGE_INTEGER MaxPasswordAge;
+ MaxPasswordAge.HighPart = 0x7FFFFFFF;
+ MaxPasswordAge.LowPart = 0xFFFFFFFF;
+
+ //
+ // PasswordDateSet should be modified to hold the last date the
+ // user's password was set.
+ //
+
+ PasswordDateSet.LowPart = 0;
+ PasswordDateSet.HighPart = 0;
+
+ if ( GetPasswordExpired( PasswordDateSet,
+ MaxPasswordAge )) {
+
+ Status = STATUS_PASSWORD_EXPIRED;
+ goto Cleanup;
+ }
+ }
+
+#endif
+
+
+#if 1
+
+ //
+ // Validate the workstation the user logged on from.
+ //
+ // Ditch leading \\ on workstation name before passing it to SAM.
+ //
+
+ LocalWorkstation = LogonNetworkInfo->Identity.Workstation;
+ if ( LocalWorkstation.Length > 0 &&
+ LocalWorkstation.Buffer[0] == L'\\' &&
+ LocalWorkstation.Buffer[1] == L'\\' ) {
+ LocalWorkstation.Buffer += 2;
+ LocalWorkstation.Length -= 2*sizeof(WCHAR);
+ LocalWorkstation.MaximumLength -= 2*sizeof(WCHAR);
+ }
+
+
+ //
+ // To validate the user's logon hours as SAM does it, use this code,
+ // otherwise, supply your own checks below this code.
+ //
+
+ Status = AccountRestrictions( UserAll->UserId,
+ &LocalWorkstation,
+ (PUNICODE_STRING) &UserAll->WorkStations,
+ &UserAll->LogonHours,
+ LogoffTime,
+ KickoffTime );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto Cleanup;
+ }
+
+#else
+
+ //
+ // Validate the user's logon hours.
+ //
+
+ if ( TRUE /* VALIDATE THE LOGON HOURS */ ) {
+
+
+ //
+ // All times are allowed, so there's no logoff
+ // time. Return forever for both logofftime and
+ // kickofftime.
+ //
+
+ LogoffTime->HighPart = 0x7FFFFFFF;
+ LogoffTime->LowPart = 0xFFFFFFFF;
+
+ KickoffTime->HighPart = 0x7FFFFFFF;
+ KickoffTime->LowPart = 0xFFFFFFFF;
+ } else {
+ Status = STATUS_INVALID_LOGON_HOURS;
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+#endif
+
+ //
+ // Validate if the user can logon from this workstation.
+ // (Supply subauthentication package specific code here.)
+
+ if ( LogonNetworkInfo->Identity.Workstation.Buffer == NULL ) {
+ Status = STATUS_INVALID_WORKSTATION;
+ *Authoritative = TRUE;
+ goto Cleanup;
+ }
+ }
+
+
+ //
+ // The user is valid.
+ //
+
+ *Authoritative = TRUE;
+ Status = STATUS_SUCCESS;
+
+ //
+ // Cleanup up before returning.
+ //
+
+Cleanup:
+
+ return Status;
+
+} // Msv1_0SubAuthenticationRoutine
+
+
+
+
+BOOL
+GetPasswordExpired (
+ IN LARGE_INTEGER PasswordLastSet,
+ IN LARGE_INTEGER MaxPasswordAge
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns true if the password is expired, false otherwise.
+
+Arguments:
+
+ PasswordLastSet - Time when the password was last set for this user.
+
+ MaxPasswordAge - Maximum password age for any password in the domain.
+
+Return Value:
+
+ Returns true if password is expired. False if not expired.
+
+--*/
+{
+ LARGE_INTEGER PasswordMustChange;
+ NTSTATUS Status;
+ BOOLEAN rc;
+ LARGE_INTEGER TimeNow;
+
+ //
+ // Compute the expiration time as the time the password was
+ // last set plus the maximum age.
+ //
+
+ if ( PasswordLastSet.QuadPart < 0 || MaxPasswordAge.QuadPart > 0 ) {
+
+ rc = TRUE; // default for invalid times is that it is expired.
+
+ } else {
+
+ __try {
+
+ PasswordMustChange.QuadPart =
+ PasswordLastSet.QuadPart - MaxPasswordAge.QuadPart;
+ //
+ // Limit the resultant time to the maximum valid absolute time
+ //
+
+ if ( PasswordMustChange.QuadPart < 0 ) {
+
+ rc = FALSE;
+
+ } else {
+
+ Status = QuerySystemTime( &TimeNow );
+ if (NT_SUCCESS(Status)) {
+
+ if ( TimeNow.QuadPart >= PasswordMustChange.QuadPart ) {
+ rc = TRUE;
+
+ } else {
+
+ rc = FALSE;
+ }
+ } else {
+ rc = FALSE; // won't fail if QuerySystemTime failed.
+ }
+ }
+
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+
+ rc = TRUE;
+ }
+ }
+
+ return rc;
+
+} // GetPasswordExpired
+
+
+NTSTATUS
+QuerySystemTime (
+ OUT PLARGE_INTEGER SystemTime
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the absolute system time. The time is in units of
+ 100nsec ticks since the base time which is midnight January 1, 1601.
+
+Arguments:
+
+ SystemTime - Supplies the address of a variable that will receive the
+ current system time.
+
+Return Value:
+
+ STATUS_SUCCESS is returned if the service is successfully executed.
+
+ STATUS_ACCESS_VIOLATION is returned if the output parameter for the
+ system time cannot be written.
+
+--*/
+
+{
+ SYSTEMTIME CurrentTime;
+
+ GetSystemTime( &CurrentTime );
+
+ if ( !SystemTimeToFileTime( &CurrentTime, (LPFILETIME) SystemTime ) ) {
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+SampMatchworkstation(
+ IN PUNICODE_STRING LogonWorkStation,
+ IN PUNICODE_STRING WorkStations
+ )
+
+/*++
+
+Routine Description:
+
+ Check if the given workstation is a member of the list of workstations
+ given.
+
+
+Arguments:
+
+ LogonWorkStations - UNICODE name of the workstation that the user is
+ trying to log into.
+
+ WorkStations - API list of workstations that the user is allowed to
+ log into.
+
+
+Return Value:
+
+
+ STATUS_SUCCESS - The user is allowed to log into the workstation.
+
+
+
+--*/
+{
+ PWCHAR WorkStationName;
+ UNICODE_STRING Unicode;
+ NTSTATUS NtStatus;
+ WCHAR Buffer[256];
+ USHORT LocalBufferLength = 256;
+ UNICODE_STRING WorkStationsListCopy;
+ BOOLEAN BufferAllocated = FALSE;
+ PWCHAR TmpBuffer;
+
+ //
+ // Local workstation is always allowed
+ // If WorkStations field is 0 everybody is allowed
+ //
+
+ if ( ( LogonWorkStation == NULL ) ||
+ ( LogonWorkStation->Length == 0 ) ||
+ ( WorkStations->Length == 0 ) ) {
+
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Assume failure; change status only if we find the string.
+ //
+
+ NtStatus = STATUS_INVALID_WORKSTATION;
+
+ //
+ // WorkStationApiList points to our current location in the list of
+ // WorkStations.
+ //
+
+ if ( WorkStations->Length > LocalBufferLength ) {
+
+ WorkStationsListCopy.Buffer = LocalAlloc( 0, WorkStations->Length );
+ BufferAllocated = TRUE;
+
+ if ( WorkStationsListCopy.Buffer == NULL ) {
+ NtStatus = STATUS_INSUFFICIENT_RESOURCES;
+ return( NtStatus );
+ }
+
+ WorkStationsListCopy.MaximumLength = WorkStations->Length;
+
+ } else {
+
+ WorkStationsListCopy.Buffer = Buffer;
+ WorkStationsListCopy.MaximumLength = LocalBufferLength;
+ }
+
+ CopyUnicodeString( &WorkStationsListCopy, WorkStations );
+
+ //
+ // wcstok requires a string the first time it's called, and NULL
+ // for all subsequent calls. Use a temporary variable so we
+ // can do this.
+ //
+
+ TmpBuffer = WorkStationsListCopy.Buffer;
+
+ while( WorkStationName = wcstok(TmpBuffer, L",") ) {
+
+ TmpBuffer = NULL;
+ InitUnicodeString( &Unicode, WorkStationName );
+ if (EqualComputerName( &Unicode, LogonWorkStation )) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if ( BufferAllocated ) {
+ LocalFree( WorkStationsListCopy.Buffer );
+ }
+
+ return( NtStatus );
+}
+
+NTSTATUS
+AccountRestrictions(
+ IN ULONG UserRid,
+ IN PUNICODE_STRING LogonWorkStation,
+ IN PUNICODE_STRING WorkStations,
+ IN PLOGON_HOURS LogonHours,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime
+ )
+
+/*++
+
+Routine Description:
+
+ Validate a user's ability to logon at this time and at the workstation
+ being logged onto.
+
+
+Arguments:
+
+ UserRid - The user id of the user to operate on.
+
+ LogonWorkStation - The name of the workstation the logon is being
+ attempted at.
+
+ WorkStations - The list of workstations the user may logon to. This
+ information comes from the user's account information. It must
+ be in API list format.
+
+ LogonHours - The times the user may logon. This information comes
+ from the user's account information.
+
+ LogoffTime - Receives the time at which the user should logoff the
+ system.
+
+ KickoffTime - Receives the time at which the user should be kicked
+ off the system.
+
+
+Return Value:
+
+
+ STATUS_SUCCESS - Logon is permitted.
+
+ STATUS_INVALID_LOGON_HOURS - The user is not authorized to logon at
+ this time.
+
+ STATUS_INVALID_WORKSTATION - The user is not authorized to logon to
+ the specified workstation.
+
+
+--*/
+{
+
+ static BOOLEAN GetForceLogoff = TRUE;
+ static LARGE_INTEGER ForceLogoff = { 0x7fffffff, 0xFFFFFFF};
+
+#define MILLISECONDS_PER_WEEK 7 * 24 * 60 * 60 * 1000
+
+ SYSTEMTIME CurrentTimeFields;
+ LARGE_INTEGER CurrentTime, CurrentUTCTime;
+ LARGE_INTEGER MillisecondsIntoWeekXUnitsPerWeek;
+ LARGE_INTEGER LargeUnitsIntoWeek;
+ LARGE_INTEGER Delta100Ns;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ ULONG CurrentMsIntoWeek;
+ ULONG LogoffMsIntoWeek;
+ ULONG DeltaMs;
+ ULONG MillisecondsPerUnit;
+ ULONG CurrentUnitsIntoWeek;
+ ULONG LogoffUnitsIntoWeek;
+ USHORT i;
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ DWORD TimeZoneId;
+ LARGE_INTEGER BiasIn100NsUnits;
+ LONG BiasInMinutes;
+
+
+
+ //
+ // Only check for users other than the builtin ADMIN
+ //
+
+ if ( UserRid != DOMAIN_USER_RID_ADMIN) {
+
+ //
+ // Scan to make sure the workstation being logged into is in the
+ // list of valid workstations - or if the list of valid workstations
+ // is null, which means that all are valid.
+ //
+
+ NtStatus = SampMatchworkstation( LogonWorkStation, WorkStations );
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ //
+ // Check to make sure that the current time is a valid time to logon
+ // in the LogonHours.
+ //
+ // We need to validate the time taking into account whether we are
+ // in daylight savings time or standard time. Thus, if the logon
+ // hours specify that we are able to logon between 9am and 5pm,
+ // this means 9am to 5pm standard time during the standard time
+ // period, and 9am to 5pm daylight savings time when in the
+ // daylight savings time. Since the logon hours stored by SAM are
+ // independent of daylight savings time, we need to add in the
+ // difference between standard time and daylight savings time to
+ // the current time before checking whether this time is a valid
+ // time to logon. Since this difference (or bias as it is called)
+ // is actually held in the form
+ //
+ // Standard time = Daylight savings time + Bias
+ //
+ // the Bias is a negative number. Thus we actually subtract the
+ // signed Bias from the Current Time.
+
+ //
+ // First, get the Time Zone Information.
+ //
+
+ TimeZoneId = GetTimeZoneInformation(
+ (LPTIME_ZONE_INFORMATION) &TimeZoneInformation
+ );
+
+ //
+ // Next, get the appropriate bias (signed integer in minutes) to subtract from
+ // the Universal Time Convention (UTC) time returned by NtQuerySystemTime
+ // to get the local time. The bias to be used depends whether we're
+ // in Daylight Savings time or Standard Time as indicated by the
+ // TimeZoneId parameter.
+ //
+ // local time = UTC time - bias in 100Ns units
+ //
+
+ switch (TimeZoneId) {
+
+ case TIME_ZONE_ID_UNKNOWN:
+
+ //
+ // There is no differentiation between standard and
+ // daylight savings time. Proceed as for Standard Time
+ //
+
+ BiasInMinutes = TimeZoneInformation.StandardBias;
+ break;
+
+ case TIME_ZONE_ID_STANDARD:
+
+ BiasInMinutes = TimeZoneInformation.StandardBias;
+ break;
+
+ case TIME_ZONE_ID_DAYLIGHT:
+
+ BiasInMinutes = TimeZoneInformation.DaylightBias;
+ break;
+
+ default:
+
+ //
+ // Something is wrong with the time zone information. Fail
+ // the logon request.
+ //
+
+ NtStatus = STATUS_INVALID_LOGON_HOURS;
+ break;
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Convert the Bias from minutes to 100ns units
+ //
+
+ BiasIn100NsUnits.QuadPart = ((LONGLONG)BiasInMinutes)
+ * 60 * 10000000;
+
+ //
+ // Get the UTC time in 100Ns units used by Windows Nt. This
+ // time is GMT.
+ //
+
+ NtStatus = QuerySystemTime( &CurrentUTCTime );
+ }
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ CurrentTime.QuadPart = CurrentUTCTime.QuadPart -
+ BiasIn100NsUnits.QuadPart;
+
+ FileTimeToSystemTime( (PFILETIME)&CurrentTime, &CurrentTimeFields );
+
+ CurrentMsIntoWeek = (((( CurrentTimeFields.wDayOfWeek * 24 ) +
+ CurrentTimeFields.wHour ) * 60 +
+ CurrentTimeFields.wMinute ) * 60 +
+ CurrentTimeFields.wSecond ) * 1000 +
+ CurrentTimeFields.wMilliseconds;
+
+ MillisecondsIntoWeekXUnitsPerWeek.QuadPart =
+ ((LONGLONG)CurrentMsIntoWeek) *
+ ((LONGLONG)LogonHours->UnitsPerWeek);
+
+ LargeUnitsIntoWeek.QuadPart =
+ MillisecondsIntoWeekXUnitsPerWeek.QuadPart / ((ULONG) MILLISECONDS_PER_WEEK);
+
+ CurrentUnitsIntoWeek = LargeUnitsIntoWeek.LowPart;
+
+ if ( !( LogonHours->LogonHours[ CurrentUnitsIntoWeek / 8] &
+ ( 0x01 << ( CurrentUnitsIntoWeek % 8 ) ) ) ) {
+
+ NtStatus = STATUS_INVALID_LOGON_HOURS;
+
+ } else {
+
+ //
+ // Determine the next time that the user is NOT supposed to be logged
+ // in, and return that as LogoffTime.
+ //
+
+ i = 0;
+ LogoffUnitsIntoWeek = CurrentUnitsIntoWeek;
+
+ do {
+
+ i++;
+
+ LogoffUnitsIntoWeek = ( LogoffUnitsIntoWeek + 1 ) % LogonHours->UnitsPerWeek;
+
+ } while ( ( i <= LogonHours->UnitsPerWeek ) &&
+ ( LogonHours->LogonHours[ LogoffUnitsIntoWeek / 8 ] &
+ ( 0x01 << ( LogoffUnitsIntoWeek % 8 ) ) ) );
+
+ if ( i > LogonHours->UnitsPerWeek ) {
+
+ //
+ // All times are allowed, so there's no logoff
+ // time. Return forever for both logofftime and
+ // kickofftime.
+ //
+
+ LogoffTime->HighPart = 0x7FFFFFFF;
+ LogoffTime->LowPart = 0xFFFFFFFF;
+
+ KickoffTime->HighPart = 0x7FFFFFFF;
+ KickoffTime->LowPart = 0xFFFFFFFF;
+
+ } else {
+
+ //
+ // LogoffUnitsIntoWeek points at which time unit the
+ // user is to log off. Calculate actual time from
+ // the unit, and return it.
+ //
+ // CurrentTimeFields already holds the current
+ // time for some time during this week; just adjust
+ // to the logoff time during this week and convert
+ // to time format.
+ //
+
+ MillisecondsPerUnit = MILLISECONDS_PER_WEEK / LogonHours->UnitsPerWeek;
+
+ LogoffMsIntoWeek = MillisecondsPerUnit * LogoffUnitsIntoWeek;
+
+ if ( LogoffMsIntoWeek < CurrentMsIntoWeek ) {
+
+ DeltaMs = MILLISECONDS_PER_WEEK - ( CurrentMsIntoWeek - LogoffMsIntoWeek );
+
+ } else {
+
+ DeltaMs = LogoffMsIntoWeek - CurrentMsIntoWeek;
+ }
+
+ Delta100Ns.QuadPart = (LONGLONG) DeltaMs * 10000;
+
+ LogoffTime->QuadPart = CurrentUTCTime.QuadPart +
+ Delta100Ns.QuadPart;
+
+ //
+ // Grab the domain's ForceLogoff time.
+ //
+
+ if ( GetForceLogoff ) {
+ NET_API_STATUS NetStatus;
+ LPUSER_MODALS_INFO_0 UserModals0;
+
+ NetStatus = NetUserModalsGet( NULL,
+ 0,
+ (LPBYTE *)&UserModals0 );
+
+ if ( NetStatus == 0 ) {
+ GetForceLogoff = FALSE;
+
+ ForceLogoff = NetpSecondsToDeltaTime( UserModals0->usrmod0_force_logoff );
+
+ NetApiBufferFree( UserModals0 );
+ }
+ }
+ //
+ // Subtract Domain->ForceLogoff from LogoffTime, and return
+ // that as KickoffTime. Note that Domain->ForceLogoff is a
+ // negative delta. If its magnitude is sufficiently large
+ // (in fact, larger than the difference between LogoffTime
+ // and the largest positive large integer), we'll get overflow
+ // resulting in a KickOffTime that is negative. In this
+ // case, reset the KickOffTime to this largest positive
+ // large integer (i.e. "never") value.
+ //
+
+
+ KickoffTime->QuadPart = LogoffTime->QuadPart - ForceLogoff.QuadPart;
+
+ if (KickoffTime->QuadPart < 0) {
+
+ KickoffTime->HighPart = 0x7FFFFFFF;
+ KickoffTime->LowPart = 0xFFFFFFFF;
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+
+ //
+ // Never kick administrators off
+ //
+
+ LogoffTime->HighPart = 0x7FFFFFFF;
+ LogoffTime->LowPart = 0xFFFFFFFF;
+ KickoffTime->HighPart = 0x7FFFFFFF;
+ KickoffTime->LowPart = 0xFFFFFFFF;
+ }
+
+
+ return( NtStatus );
+}
+
+LARGE_INTEGER
+NetpSecondsToDeltaTime(
+ IN ULONG Seconds
+ )
+
+/*++
+
+Routine Description:
+
+ Convert a number of seconds to an NT delta time specification
+
+Arguments:
+
+ Seconds - a positive number of seconds
+
+Return Value:
+
+ Returns the NT Delta time. NT delta time is a negative number
+ of 100ns units.
+
+--*/
+
+{
+ LARGE_INTEGER DeltaTime;
+ LARGE_INTEGER LargeSeconds;
+ LARGE_INTEGER Answer;
+
+ //
+ // Special case TIMEQ_FOREVER (return a full scale negative)
+ //
+
+ if ( Seconds == TIMEQ_FOREVER ) {
+ DeltaTime.LowPart = 0;
+ DeltaTime.HighPart = (LONG) 0x80000000;
+
+ //
+ // Convert seconds to 100ns units simply by multiplying by 10000000.
+ //
+ // Convert to delta time by negating.
+ //
+
+ } else {
+
+ LargeSeconds.LowPart = Seconds;
+ LargeSeconds.HighPart = 0;
+
+ Answer.QuadPart = LargeSeconds.QuadPart * 10000000;
+
+ if ( Answer.QuadPart < 0 ) {
+ DeltaTime.LowPart = 0;
+ DeltaTime.HighPart = (LONG) 0x80000000;
+ } else {
+ DeltaTime.QuadPart = -Answer.QuadPart;
+ }
+
+ }
+
+ return DeltaTime;
+
+} // NetpSecondsToDeltaTime
+
+
+BOOLEAN
+EqualComputerName(
+ IN PUNICODE_STRING String1,
+ IN PUNICODE_STRING String2
+ )
+/*++
+
+Routine Description:
+
+ Compare two computer names for equality.
+
+Arguments:
+
+ String1 - Name of first computer.
+ String2 - Name of second computer.
+
+Return Value:
+
+ TRUE if the names, converted to OEM, compare case-insensitively,
+ FALSE if they don't compare or can't be converted to OEM.
+
+--*/
+{
+ WCHAR Computer1[CNLEN+1];
+ WCHAR Computer2[CNLEN+1];
+ CHAR OemComputer1[CNLEN+1];
+ CHAR OemComputer2[CNLEN+1];
+
+ //
+ // Make sure the names are not too long
+ //
+
+ if ((String1->Length > CNLEN*sizeof(WCHAR)) ||
+ (String2->Length > CNLEN*sizeof(WCHAR))) {
+ return(FALSE);
+
+ }
+
+ //
+ // Copy them to null terminated strings
+ //
+
+ CopyMemory(
+ Computer1,
+ String1->Buffer,
+ String1->Length
+ );
+ Computer1[String1->Length/sizeof(WCHAR)] = L'\0';
+
+ CopyMemory(
+ Computer2,
+ String2->Buffer,
+ String2->Length
+ );
+ Computer2[String2->Length/sizeof(WCHAR)] = L'\0';
+
+ //
+ // Convert the computer names to OEM
+ //
+
+ if (!CharToOemW(
+ Computer1,
+ OemComputer1
+ )) {
+ return(FALSE);
+ }
+
+ if (!CharToOemW(
+ Computer2,
+ OemComputer2
+ )) {
+ return(FALSE);
+ }
+
+ //
+ // Do a case insensitive comparison of the oem computer names.
+ //
+
+ if (lstrcmpiA(OemComputer1, OemComputer2) == 0)
+ {
+ return(TRUE);
+ }
+ else
+ {
+ return(FALSE);
+ }
+}
+
+VOID
+InitUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PCWSTR SourceString OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The InitUnicodeString function initializes an NT counted
+ unicode string. The DestinationString is initialized to point to
+ the SourceString and the Length and MaximumLength fields of
+ DestinationString are initialized to the length of the SourceString,
+ which is zero if SourceString is not specified.
+
+Arguments:
+
+ DestinationString - Pointer to the counted string to initialize
+
+ SourceString - Optional pointer to a null terminated unicode string that
+ the counted string is to point to.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG Length;
+
+ DestinationString->Buffer = (PWSTR)SourceString;
+ if (SourceString != NULL) {
+ Length = wcslen( SourceString ) * sizeof( WCHAR );
+ DestinationString->Length = (USHORT)Length;
+ DestinationString->MaximumLength = (USHORT)(Length + sizeof(UNICODE_NULL));
+ }
+ else {
+ DestinationString->MaximumLength = 0;
+ DestinationString->Length = 0;
+ }
+}
+
+VOID
+CopyUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The CopyString function copies the SourceString to the
+ DestinationString. If SourceString is not specified, then
+ the Length field of DestinationString is set to zero. The
+ MaximumLength and Buffer fields of DestinationString are not
+ modified by this function.
+
+ The number of bytes copied from the SourceString is either the
+ Length of SourceString or the MaximumLength of DestinationString,
+ whichever is smaller.
+
+Arguments:
+
+ DestinationString - Pointer to the destination string.
+
+ SourceString - Optional pointer to the source string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ UNALIGNED WCHAR *src, *dst;
+ ULONG n;
+
+ if (SourceString != NULL) {
+ dst = DestinationString->Buffer;
+ src = SourceString->Buffer;
+ n = SourceString->Length;
+ if ((USHORT)n > DestinationString->MaximumLength) {
+ n = DestinationString->MaximumLength;
+ }
+
+ DestinationString->Length = (USHORT)n;
+ CopyMemory(dst, src, n);
+ if (DestinationString->Length < DestinationString->MaximumLength) {
+ dst[n / sizeof(WCHAR)] = UNICODE_NULL;
+ }
+
+ } else {
+ DestinationString->Length = 0;
+ }
+
+ return;
+}
+
+
+NTSTATUS
+Msv1_0SubAuthenticationFilter (
+ IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
+ IN PVOID LogonInformation,
+ IN ULONG Flags,
+ IN PUSER_ALL_INFORMATION UserAll,
+ OUT PULONG WhichFields,
+ OUT PULONG UserFlags,
+ OUT PBOOLEAN Authoritative,
+ OUT PLARGE_INTEGER LogoffTime,
+ OUT PLARGE_INTEGER KickoffTime
+)
+{
+ return( Msv1_0SubAuthenticationRoutine(
+ LogonLevel,
+ LogonInformation,
+ Flags,
+ UserAll,
+ WhichFields,
+ UserFlags,
+ Authoritative,
+ LogoffTime,
+ KickoffTime
+ ) );
+}
+// subauth.c eof
+
diff --git a/private/lsa/msv1_0/subauth/subauth.def b/private/lsa/msv1_0/subauth/subauth.def
new file mode 100644
index 000000000..0db64419e
--- /dev/null
+++ b/private/lsa/msv1_0/subauth/subauth.def
@@ -0,0 +1,8 @@
+LIBRARY SubAuth
+
+DESCRIPTION 'Microsoft Authentication Package V1_0 SubAuthentication DLL'
+
+EXPORTS
+ Msv1_0SubAuthenticationRoutine
+ Msv1_0SubAuthenticationFilter
+
diff --git a/private/lsa/server/adt.h b/private/lsa/server/adt.h
new file mode 100644
index 000000000..3f134ec7c
--- /dev/null
+++ b/private/lsa/server/adt.h
@@ -0,0 +1,155 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adt.h
+
+Abstract:
+
+ Local Security Authority - Audit Log Management - Public Defines,
+ data and function prototypes.
+
+ Functions, data and defines in this module are exported to the
+ whole of the Lsa subsystem from the Auditing Sub-component.
+
+Author:
+
+ Scott Birrell (ScottBi) November 20, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+//
+// Initialization Pass for Auditing.
+//
+
+extern ULONG LsapAdtInitializationPass;
+
+
+//
+// Flag indicating whether shutdown is in progress
+//
+
+extern BOOLEAN LsapShutdownInProgress;
+
+//
+// Audit Log Information. This must be kept in sync with the information
+// in the Lsa Database.
+//
+
+extern POLICY_AUDIT_LOG_INFO LsapAdtLogInformation;
+
+extern LSARM_POLICY_AUDIT_EVENTS_INFO LsapAdtEventsInformation;
+
+//
+// Audit Log Full Information.
+//
+
+extern POLICY_AUDIT_FULL_QUERY_INFO LsapAdtLogFullInformation;
+
+//
+// Audit Log Maximum Record Id. Audit Records are numbered serially until
+// this limit is reached, then numbering wraps to 0.
+//
+
+#define LSAP_ADT_MAXIMUM_RECORD_ID (0x7fffffffL)
+
+//
+// Flag for console handler indicating how late we should be shutdown.
+// This number is intentionally lower than the value used by the
+// the service controller.
+//
+
+#define LSAP_SHUTDOWN_LEVEL 400
+
+//
+// Options for LsapAdtQueryAuditLogFullInfo
+//
+
+#define LSAP_ADT_LOG_FULL_UPDATE ((ULONG)(0x00000001L))
+
+
+NTSTATUS
+LsapAdtWriteLogWrkr(
+ IN PLSA_COMMAND_MESSAGE CommandMessage,
+ OUT PLSA_REPLY_MESSAGE ReplyMessage
+ );
+
+NTSTATUS
+LsapAdtSetInfoLog(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PPOLICY_AUDIT_LOG_INFO PolicyAuditLogInfo
+ );
+
+NTSTATUS
+LsapAdtInitialize(
+ IN ULONG Pass
+ );
+
+NTSTATUS
+LsapAdtInitializeDefaultAuditing(
+ IN ULONG Options,
+ OUT PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInformation
+ );
+
+VOID
+LsapAdtAuditingLogon(
+ PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo
+ );
+
+
+VOID
+LsapAdtAuditPackageLoad(
+ PUNICODE_STRING PackageFileName
+ );
+
+NTSTATUS
+LsapAdtQueryAuditLogFullInfo(
+ IN PLSAPR_HANDLE PolicyHandle,
+ IN ULONG Options,
+ OUT PPOLICY_AUDIT_FULL_QUERY_INFO PolicyAuditFullQueryInfo
+ );
+
+NTSTATUS
+LsapAdtGenerateLsaAuditEvent(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG AuditEventCategory,
+ IN ULONG AuditEventId,
+ IN PPRIVILEGE_SET Privileges,
+ IN ULONG SidCount,
+ IN PSID *Sids OPTIONAL,
+ IN ULONG UnicodeStringCount,
+ IN PUNICODE_STRING UnicodeStrings OPTIONAL,
+ IN PLSARM_POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo OPTIONAL
+ );
+
+#define LsapAdtAuditingEnabled() \
+ (LsapAdtEventsInformation.AuditingMode)
+
+#define LsapAdtAuditingPolicyChanges() \
+ (LsapAdtAuditingEnabled() && \
+ (LsapAdtEventsInformation.EventAuditingOptions[ AuditCategoryPolicyChange ] & POLICY_AUDIT_EVENT_SUCCESS))
+
+
+//
+// Macro to determine the size of a PRIVILEGE_SET
+//
+
+#define LsapPrivilegeSetSize( PrivilegeSet ) \
+ ( ( PrivilegeSet ) == NULL ? 0 : \
+ ((( PrivilegeSet )->PrivilegeCount > 0) \
+ ? \
+ ((ULONG)sizeof(PRIVILEGE_SET) + \
+ ( \
+ (( PrivilegeSet )->PrivilegeCount - ANYSIZE_ARRAY) * \
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) \
+ ) \
+ ) \
+ : ((ULONG)sizeof(PRIVILEGE_SET) - (ULONG)sizeof(LUID_AND_ATTRIBUTES)) \
+ ))
+
diff --git a/private/lsa/server/adtbuild.c b/private/lsa/server/adtbuild.c
new file mode 100644
index 000000000..648ee4007
--- /dev/null
+++ b/private/lsa/server/adtbuild.c
@@ -0,0 +1,624 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtbuild.c
+
+Abstract:
+
+ Local Security Authority - Audit Log Management
+
+ Functions in this module build unicode strings for
+ various parameter types. Some parameter string build
+ routines may also be found in other modules (such as
+ LsapAdtBuildAccessesString() in adtobjs.c).
+
+Author:
+
+ Jim Kelly (JimK) 29-Oct-1992
+
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <msaudite.h>
+#include "lsasrvp.h"
+#include "adtp.h"
+#include "ausrvp.h"
+
+#ifndef LSAP_ADT_UMTEST
+//
+// pick up definitions of privately callable LSA services ONLY if we
+// aren't building a user mode test.
+//
+
+#include "aup.h"
+
+#endif
+
+
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Local Macro definitions and local function prototypes //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+#ifdef LSAP_ADT_UMTEST
+
+//
+// Define all external routines that we won't pick up in a user mode test
+//
+
+NTSTATUS
+LsapGetLogonSessionAccountInfo(
+ IN PLUID Value,
+ OUT PUNICODE_STRING AccountName,
+ OUT PUNICODE_STRING AuthorityName
+ );
+
+
+
+#endif
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Data types used within this module //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Variables global within this module //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Services exported by this module. //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapAdtBuildUlongString(
+ IN ULONG Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a unicode string representing the passed value.
+
+ The resultant string will be formatted as a decimal value with not
+ more than 10 digits.
+
+
+Arguments:
+
+ Value - The value to be transformed to printable format (Unicode string).
+
+ ResultantString - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ FreeWhenDone - If TRUE, indicates that the body of the ResultantString
+ must be freed to process heap when no longer needed.
+
+
+Return Values:
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ for the string body.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+
+
+ //
+ // Maximum length is 10 wchar characters plus a null termination character.
+ //
+
+ ResultantString->Length = 0;
+ ResultantString->MaximumLength = 11 * sizeof(WCHAR); // 10 digits & null termination
+
+ ResultantString->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0,
+ ResultantString->MaximumLength);
+ if (ResultantString->Buffer == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+
+
+
+ Status = RtlIntegerToUnicodeString( Value, 10, ResultantString );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ (*FreeWhenDone) = TRUE;
+ return(STATUS_SUCCESS);
+
+
+
+}
+
+
+NTSTATUS
+LsapAdtBuildLuidString(
+ IN PLUID Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a unicode string representing the passed LUID.
+
+ The resultant string will be formatted as follows:
+
+ (0x00005678,0x12340000)
+
+Arguments:
+
+ Value - The value to be transformed to printable format (Unicode string).
+
+ ResultantString - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ FreeWhenDone - If TRUE, indicates that the body of the ResultantString
+ must be freed to process heap when no longer needed.
+
+
+Return Values:
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ for the string body.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ UNICODE_STRING IntegerString;
+
+
+ ULONG Buffer[(16*sizeof(WCHAR))/sizeof(ULONG)];
+
+
+ IntegerString.Buffer = (PWCHAR)&Buffer[0];
+ IntegerString.MaximumLength = 16*sizeof(WCHAR);
+
+
+ //
+ // Length (in WCHARS) is 3 for (0x
+ // 10 for 1st hex number
+ // 3 for ,0x
+ // 10 for 2nd hex number
+ // 1 for )
+ // 1 for null termination
+ //
+
+ ResultantString->Length = 0;
+ ResultantString->MaximumLength = 28 * sizeof(WCHAR);
+
+ ResultantString->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0,
+ ResultantString->MaximumLength);
+ if (ResultantString->Buffer == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"(0x" );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ Status = RtlIntegerToUnicodeString( Value->HighPart, 16, &IntegerString );
+ ASSERT(NT_SUCCESS(Status));
+ Status = RtlAppendUnicodeToString( ResultantString, IntegerString.Buffer );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ Status = RtlAppendUnicodeToString( ResultantString, L",0x" );
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlIntegerToUnicodeString( Value->LowPart, 16, &IntegerString );
+ ASSERT(NT_SUCCESS(Status));
+ Status = RtlAppendUnicodeToString( ResultantString, IntegerString.Buffer );
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlAppendUnicodeToString( ResultantString, L")" );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ (*FreeWhenDone) = TRUE;
+ return(STATUS_SUCCESS);
+
+
+
+}
+
+
+NTSTATUS
+LsapAdtBuildSidString(
+ IN PSID Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a unicode string representing the passed LUID.
+
+ The resultant string will be formatted as follows:
+
+ S-1-281736-12-72-9-110
+ ^ ^^ ^^ ^ ^^^
+ | | | | |
+ +-----+--+-+--+---- Decimal
+
+Arguments:
+
+ Value - The value to be transformed to printable format (Unicode string).
+
+ ResultantString - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ FreeWhenDone - If TRUE, indicates that the body of the ResultantString
+ must be freed to process heap when no longer needed.
+
+
+Return Values:
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ for the string body.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+
+ //
+ // Tell the RTL routine to allocate the buffer.
+ //
+
+ Status = RtlConvertSidToUnicodeString( ResultantString, Value, TRUE );
+
+
+
+ (*FreeWhenDone) = TRUE;
+ return(Status);
+
+
+
+}
+
+
+
+NTSTATUS
+LsapAdtBuildDashString(
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns a string containing a dash ("-").
+ This is commonly used to represent "No value" in audit records.
+
+
+Arguments:
+
+
+ ResultantString - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ FreeWhenDone - If TRUE, indicates that the body of the ResultantString
+ must be freed to process heap when no longer needed.
+
+
+Return Values:
+
+ STATUS_SUCCESS only.
+
+--*/
+
+{
+// NTSTATUS Status;
+
+
+ RtlInitUnicodeString(ResultantString, L"-");
+
+ (*FreeWhenDone) = FALSE;
+ return(TRUE);
+
+
+
+}
+
+
+
+
+NTSTATUS
+LsapAdtBuildFilePathString(
+ IN PUNICODE_STRING Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a unicode string representing the passed file
+ path name. If possible, the string will be generated using drive
+ letters instead of object architecture namespace.
+
+
+Arguments:
+
+ Value - The original file path name. This is expected (but does not
+ have to be) a standard NT object architecture name-space pathname.
+
+ ResultantString - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ FreeWhenDone - If TRUE, indicates that the body of the ResultantString
+ must be freed to process heap when no longer needed.
+
+
+Return Values:
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ for the string body.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+
+
+ //
+ // For now, don't do the conversion.
+ // Do this if we have time before we ship.
+ //
+
+ ResultantString->Length = Value->Length;
+ ResultantString->Buffer = Value->Buffer;
+ ResultantString->MaximumLength = Value->MaximumLength;
+
+
+ (*FreeWhenDone) = FALSE;
+ return(Status);
+}
+
+
+
+
+NTSTATUS
+LsapAdtBuildLogonIdStrings(
+ IN PLUID LogonId,
+ OUT PUNICODE_STRING ResultantString1,
+ OUT PBOOLEAN FreeWhenDone1,
+ OUT PUNICODE_STRING ResultantString2,
+ OUT PBOOLEAN FreeWhenDone2,
+ OUT PUNICODE_STRING ResultantString3,
+ OUT PBOOLEAN FreeWhenDone3
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a 3 unicode strings representing the specified
+ logon ID. These strings will contain the username, domain, and
+ LUID string of the specified logon session (respectively).
+
+
+Arguments:
+
+ Value - The logon ID.
+
+ ResultantString1 - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ This parameter will contain the username.
+
+
+ FreeWhenDone1 - If TRUE, indicates that the body of ResultantString1
+ must be freed to process heap when no longer needed.
+
+ ResultantString2 - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ This parameter will contain the username.
+
+
+ FreeWhenDone2 - If TRUE, indicates that the body of ResultantString2
+ must be freed to process heap when no longer needed.
+
+ ResultantString3 - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ This parameter will contain the username.
+
+
+ FreeWhenDone3 - If TRUE, indicates that the body of ResultantString3
+ must be freed to process heap when no longer needed.
+
+
+Return Values:
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ for the string body.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Try to convert the LUID first.
+ //
+
+ Status = LsapAdtBuildLuidString( LogonId, ResultantString3, FreeWhenDone3 );
+
+ if (NT_SUCCESS(Status)) {
+
+
+ //
+ // Now get the username and domain names
+ //
+
+ Status = LsapGetLogonSessionAccountInfo( LogonId,
+ ResultantString1,
+ ResultantString2
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ (*FreeWhenDone1) = TRUE;
+ (*FreeWhenDone2) = TRUE;
+
+ } else {
+
+ //
+ // The LUID may be the system LUID
+ //
+
+ LUID SystemLuid = SYSTEM_LUID;
+
+ if ( RtlEqualLuid( LogonId, &SystemLuid )) {
+
+ RtlInitUnicodeString(ResultantString1, L"SYSTEM");
+ RtlInitUnicodeString(ResultantString2, L"SYSTEM");
+
+ (*FreeWhenDone1) = FALSE;
+ (*FreeWhenDone2) = FALSE;
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // We have no clue what this is, just free what we've
+ // allocated.
+ //
+
+ if ((FreeWhenDone3)) {
+ LsapFreeLsaHeap( ResultantString3->Buffer );
+ }
+ }
+ }
+ }
+
+ return(Status);
+
+}
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Services private to this module. //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+
+#ifdef LSAP_ADT_UMTEST
+
+//
+// Define this routine only for user mode test
+//
+
+NTSTATUS
+LsapGetLogonSessionAccountInfo(
+ IN PLUID Value,
+ OUT PUNICODE_STRING AccountName,
+ OUT PUNICODE_STRING AuthorityName
+ )
+
+{
+
+ NTSTATUS Status = STATUS_NO_MEMORY;
+
+
+ if (RtlCreateUnicodeString( AccountName, L"Bullwinkle" )) {
+ if (RtlCreateUnicodeString( AuthorityName, L"The Rocky Show" )) {
+ Status = STATUS_SUCCESS;
+ } else {
+ RtlFreeHeap( RtlProcessHeap(), Accountame->Buffer );
+ }
+ }
+
+ return(Status);
+
+
+
+
+}
+#endif
diff --git a/private/lsa/server/adtevent.c b/private/lsa/server/adtevent.c
new file mode 100644
index 000000000..3accb3b36
--- /dev/null
+++ b/private/lsa/server/adtevent.c
@@ -0,0 +1,636 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtevent.c
+
+Abstract:
+
+ Local Security Authority - Audit Event Management
+
+ Functions in this module access the Audit Log via the Event Logging
+ interface.
+
+Author:
+
+ Scott Birrell (ScottBi) January 19, 1993
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <msaudite.h>
+#include "lsasrvp.h"
+#include "ausrvp.h"
+#include "adtp.h"
+
+
+NTSTATUS
+LsapAdtGenerateLsaAuditEvent(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG AuditEventCategory,
+ IN ULONG AuditEventId,
+ IN PPRIVILEGE_SET Privileges,
+ IN ULONG SidCount,
+ IN PSID *Sids OPTIONAL,
+ IN ULONG UnicodeStringCount,
+ IN PUNICODE_STRING UnicodeStrings OPTIONAL,
+ IN PLSARM_POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates an Lsa-originated Audit Event. Audit Events
+ of this kind are generated as a result of Local Security Policy changes
+ such as assigning/removing user rights to an account.
+
+Arguments:
+
+ ObjectHandle - Specifies the handle of an object in the Lsa Policy
+ Database. For global changes to policy, a handle to the
+ Lsa Policy object is passed.
+
+ AuditEventCategory - Specifies the Id of the Audit Event Category
+ to which this Audit Event belongs.
+
+ AuditEventId - Specifies the Id of the Audit Event being generated.
+
+ LuidCount - Count of Locally Unique Ids being passed via the Luids
+ parameter. If no Locally Unique Ids are passed, this parameter must
+ be set to 0.
+
+ Luids - Pointer to array of LuidCount Locally Unique Ids and their attributes.
+ The attributes are ignored. If 0 is passed for the LuidCount
+ parameter, this parameter is ignored and NULL may be specified.
+
+ SidCount - Count of Sids being passed via the Sids parameter. If no
+ Sids are passed, this parameter must be set to 0.
+
+ Sids - Pointer to array of SidCount Sids. If 0 is passed for the
+ SidCount parameter, this parameter is ignored and NULL may be
+ specified.
+
+ UnicodeStringCount - Count of Unicode Strings being passed via the
+ UnicodeStrings parameter. If no Unicode Strings are passed, this
+ parameter must be set to 0.
+
+ UnicodeStrings - Pointer to array of UnicodeStringCount strings. If 0 is
+ passed for the SidCount parameter, this parameter is ignored and NULL
+ may be specified.
+
+ PolicyAuditEventsInfo - Pointer to Auditing Events information structure
+ containing the AuditingMode and the array of Policy Audit Event
+ Information entries. This parameter must be non-NULL if and only if
+ the AuditEventCategory parameter is SE_AUDIT_POLICY_CHANGE.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LUID ClientAuthenticationId;
+ PTOKEN_USER TokenUserInformation;
+ PSID ClientSid;
+
+
+ Status = LsapQueryClientInfo(
+ &TokenUserInformation,
+ &ClientAuthenticationId
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // We can't generate an audit without a
+ // user Sid.
+ //
+
+ return( Status );
+ }
+
+ ClientSid = TokenUserInformation->User.Sid;
+
+ switch ( AuditEventCategory ) {
+ case SE_CATEGID_POLICY_CHANGE:
+ {
+ switch ( AuditEventId ) {
+ case SE_AUDITID_POLICY_CHANGE:
+ {
+
+ (VOID) LsapAdtPolicyChange(
+ (USHORT)AuditEventCategory,
+ AuditEventId,
+ EVENTLOG_AUDIT_SUCCESS,
+ ClientSid,
+ ClientAuthenticationId,
+ PolicyAuditEventsInfo
+ );
+
+ break;
+ }
+ case SE_AUDITID_TRUSTED_DOMAIN_REM:
+ case SE_AUDITID_TRUSTED_DOMAIN_ADD:
+ {
+
+ (VOID) LsapAdtTrustedDomain(
+ (USHORT)AuditEventCategory,
+ AuditEventId,
+ EVENTLOG_AUDIT_SUCCESS,
+ ClientSid,
+ ClientAuthenticationId,
+ Sids[0],
+ UnicodeStrings
+ );
+
+
+ break;
+ }
+ case SE_AUDITID_USER_RIGHT_ASSIGNED:
+ case SE_AUDITID_USER_RIGHT_REMOVED:
+ {
+
+ (VOID) LsapAdtUserRightAssigned(
+ (USHORT)AuditEventCategory,
+ AuditEventId,
+ EVENTLOG_AUDIT_SUCCESS,
+ ClientSid,
+ ClientAuthenticationId,
+ Sids[0],
+ Privileges
+ );
+
+ break;
+
+ }
+ }
+
+
+ break;
+
+ }
+ default:
+ {
+
+ return( STATUS_SUCCESS );
+ }
+ }
+
+
+ //
+ // Avoid unreferenced label.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GenerateLsaAuditEventError;
+ }
+
+ //
+ // Put cleanup that happens in the success case only here
+ //
+
+GenerateLsaAuditEventFinish:
+
+ //
+ // Put cleanup that happens in success or error cases here
+ //
+
+ if ( TokenUserInformation != NULL ) {
+ LsapFreeLsaHeap( TokenUserInformation );
+ }
+
+ return(Status);
+
+GenerateLsaAuditEventError:
+
+ //
+ // Put cleanup that happens in the error case only here.
+ //
+
+ goto GenerateLsaAuditEventFinish;
+}
+
+
+VOID
+LsapAdtUserRightAssigned(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PSID ClientSid,
+ IN LUID CallerAuthenticationId,
+ IN PSID TargetSid,
+ IN PPRIVILEGE_SET Privileges
+ )
+/*++
+
+Routine Description:
+
+ Generates an audit for a user right being either assigned or removed.
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ //
+ // Build an audit parameters structure.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = EventCategory;
+ AuditParameters.AuditId = EventID;
+ AuditParameters.Type = EventType;
+ AuditParameters.ParameterCount = 0;
+
+ //
+ // User Sid
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, ClientSid );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Subsystem name (if available)
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Rights
+ //
+
+ LsapSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, Privileges );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Target Sid
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, TargetSid );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Caller's Authentication information
+ //
+
+ LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, CallerAuthenticationId );
+ AuditParameters.ParameterCount++;
+
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ return;
+}
+
+
+VOID
+LsapAdtTrustedDomain(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PSID ClientSid,
+ IN LUID CallerAuthenticationId,
+ IN PSID TargetSid,
+ IN PUNICODE_STRING DomainName
+ )
+
+/*++
+
+Routine Description:
+
+ Generates an audit for a trusted being either assigned or removed.
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ //
+ // Build an audit parameters structure.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = EventCategory;
+ AuditParameters.AuditId = EventID;
+ AuditParameters.Type = EventType;
+ AuditParameters.ParameterCount = 0;
+
+ //
+ // User Sid
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, ClientSid );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Subsystem name (if available)
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Rights
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, DomainName );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Target Sid
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, TargetSid );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Caller's Authentication information
+ //
+
+ LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, CallerAuthenticationId );
+ AuditParameters.ParameterCount++;
+
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ return;
+}
+
+
+VOID
+LsapAdtPolicyChange(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PSID ClientSid,
+ IN LUID CallerAuthenticationId,
+ IN PLSARM_POLICY_AUDIT_EVENTS_INFO LsapAdtEventsInformation
+ )
+/*++
+
+Routine Description:
+
+ Generates an audit for a policy change event.
+
+Arguments:
+
+ EventCategory - The category of this audit.
+
+ EventID - The event we are auditing.
+
+ EventType - Whether the audit is success or failure.
+
+ ClientSid - The SID of the user performing the policy change.
+
+ CallerAuthenticationId - The Authentication id of the user.
+
+ LsapAdtEventsInformation - The information to audit.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ UNICODE_STRING Enabled;
+ UNICODE_STRING Disabled;
+ ULONG i;
+
+ RtlInitUnicodeString( &Enabled, L"+" );
+ RtlInitUnicodeString( &Disabled, L"-" );
+ EventAuditingOptions = LsapAdtEventsInformation->EventAuditingOptions;
+
+ //
+ // Build an audit parameters structure.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = EventCategory;
+ AuditParameters.AuditId = EventID;
+ AuditParameters.Type = EventType;
+ AuditParameters.ParameterCount = 0;
+
+ //
+ // User Sid
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, ClientSid );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Subsystem name (if available)
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ //
+ // If auditing is disabled, mark all options as disabled. Otherwise
+ // mark them as the appropriate
+ //
+
+ if (LsapAdtEventsInformation->AuditingMode) {
+ for ( i=0; i<POLICY_AUDIT_EVENT_TYPE_COUNT; i++ ) {
+
+ LsapSetParmTypeString(
+ AuditParameters,
+ AuditParameters.ParameterCount,
+ (EventAuditingOptions[i] & POLICY_AUDIT_EVENT_SUCCESS ? &Enabled : &Disabled)
+ );
+
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString(
+ AuditParameters,
+ AuditParameters.ParameterCount,
+ (EventAuditingOptions[i] & POLICY_AUDIT_EVENT_FAILURE ? &Enabled : &Disabled)
+ );
+
+ AuditParameters.ParameterCount++;
+ }
+ } else {
+ //
+ // Auditing is disabled - mark them all disabled.
+ //
+
+ for ( i=0; i<POLICY_AUDIT_EVENT_TYPE_COUNT; i++ ) {
+
+ LsapSetParmTypeString(
+ AuditParameters,
+ AuditParameters.ParameterCount,
+ &Disabled
+ );
+
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString(
+ AuditParameters,
+ AuditParameters.ParameterCount,
+ &Disabled
+ );
+
+ AuditParameters.ParameterCount++;
+ }
+
+ }
+
+ //
+ // Caller's Authentication information
+ //
+
+ LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, CallerAuthenticationId );
+ AuditParameters.ParameterCount++;
+
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ return;
+}
+
+
+NTSTATUS
+LsapQueryClientInfo(
+ PTOKEN_USER *UserSid,
+ PLUID AuthenticationId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine impersonates our client, opens the thread token, and
+ extracts the User Sid. It puts the Sid in memory allocated via
+ LsapAllocateLsaHeap, which must be freed by the caller.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns a pointer to heap memory containing a copy of the Sid, or
+ NULL.
+
+--*/
+
+{
+ NTSTATUS Status;
+ HANDLE TokenHandle;
+ ULONG ReturnLength;
+ TOKEN_STATISTICS TokenStats;
+
+ Status = (NTSTATUS)RpcImpersonateClient( NULL );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ TRUE, // OpenAsSelf
+ &TokenHandle
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = (NTSTATUS)RpcRevertToSelf();
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken (
+ TokenHandle,
+ TokenUser,
+ NULL,
+ 0,
+ &ReturnLength
+ );
+
+ if ( Status != STATUS_BUFFER_TOO_SMALL ) {
+
+ NtClose( TokenHandle );
+ return( Status );
+ }
+
+ *UserSid = LsapAllocateLsaHeap( ReturnLength );
+
+ if ( *UserSid == NULL ) {
+
+ NtClose( TokenHandle );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Status = NtQueryInformationToken (
+ TokenHandle,
+ TokenUser,
+ *UserSid,
+ ReturnLength,
+ &ReturnLength
+ );
+
+
+ if ( !NT_SUCCESS( Status )) {
+
+ NtClose( TokenHandle );
+ LsapFreeLsaHeap( *UserSid );
+ return( Status );
+ }
+
+ Status = NtQueryInformationToken (
+ TokenHandle,
+ TokenStatistics,
+ (PVOID)&TokenStats,
+ sizeof( TOKEN_STATISTICS ),
+ &ReturnLength
+ );
+
+ NtClose( TokenHandle );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ LsapFreeLsaHeap( *UserSid );
+ return( Status );
+ }
+
+ *AuthenticationId = TokenStats.AuthenticationId;
+
+ return( STATUS_SUCCESS );
+}
+
+
+
diff --git a/private/lsa/server/adtinit.c b/private/lsa/server/adtinit.c
new file mode 100644
index 000000000..dce198800
--- /dev/null
+++ b/private/lsa/server/adtinit.c
@@ -0,0 +1,878 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtinit.c
+
+Abstract:
+
+ Local Security Authority - Auditing Initialization
+
+Author:
+
+ Scott Birrell (ScottBi) November 20, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#include <msaudite.h>
+#include "lsasrvp.h"
+#include "adtp.h"
+
+NTSTATUS LsapAdtInitializeCrashOnFail( VOID );
+BOOL LsapShutdownNotification( IN ULONG ControlType );
+
+BOOLEAN LsapShutdownInProgress = FALSE;
+ULONG LsapAdtInitializationPass = 0;
+
+
+//
+// Array of drive letter to device mappings for generating path strings.
+//
+
+DRIVE_MAPPING DriveMappingArray[MAX_DRIVE_MAPPING];
+
+
+//
+// Name that will be used as the default subsystem name for LSA generated events
+//
+
+UNICODE_STRING LsapSubsystemName;
+
+
+//
+// Special privilege values which are not normally audited,
+// but generate audits when assigned to a user. See
+// LsapAdtAuditSpecialPrivileges.
+//
+
+LUID ChangeNotifyPrivilege;
+LUID AuditPrivilege;
+LUID CreateTokenPrivilege;
+LUID AssignPrimaryTokenPrivilege;
+LUID BackupPrivilege;
+LUID RestorePrivilege;
+LUID DebugPrivilege;
+
+
+//
+// Global variable indicating whether or not we are supposed
+// to crash when an audit fails.
+//
+
+BOOLEAN LsapCrashOnAuditFail = FALSE;
+BOOLEAN LsapAllowAdminLogonsOnly = FALSE;
+
+
+
+NTSTATUS
+LsapAdtInitialize(
+ IN ULONG Pass
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs initialization of auditing within the LSA, and
+ it also issues commands to the Reference Monitor to enable it to
+ complete any initialization of auditing variables that is dependent
+ on the content of the LSA Database. At time of call, the main
+ System Init thread is in the Reference Monitor awaiting completion
+ of all LSA initialization, and the Reference Monitor Command
+ Server thread is waiting for commands.
+
+ The following steps are performed:
+
+ o Read the Audit Event and Audit Log information from the LSA
+ Database.
+ o Call the Event Logging function to open the Audit Log
+ o Issue a Reference Monitor command to write the Audit Event Info
+ to the Reference-Monitor's in-memory database.
+
+Arguments:
+
+ Pass - Specifies the stage of initialization to be performed.
+
+ Pass 1 - Initialization required before Audit Records can
+ be written to the Audit Log. Any Audit Records received
+ during this time will be "cached" by the LSA and will
+ be written out at Pass 2.
+
+ Pass 2 - Write out Audit Records cached during Pass 1.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ ULONG AuditLogInfoLength = sizeof (POLICY_AUDIT_LOG_INFO);
+ ULONG AuditEventInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
+ ULONG AuditFullQueryInfoLength = sizeof (POLICY_AUDIT_FULL_QUERY_INFO);
+ BOOLEAN AcquiredLock = FALSE;
+ UNICODE_STRING UnicodeString;
+ PUNICODE_STRING Strings;
+ PSID Sid = NULL;
+ LSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo;
+
+ Strings = &UnicodeString;
+
+ RtlInitUnicodeString( Strings, L"System Restart");
+
+ RtlInitUnicodeString( &LsapSubsystemName, L"Security" );
+
+ if (Pass == 1) {
+
+ Status = LsapAdtInitializeLogQueue();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto AuditInitError;
+ }
+
+ //
+ // Acquire the LSA Database Lock.
+ //
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto AuditInitError;
+ }
+
+ AcquiredLock = TRUE;
+
+ //
+ // Read the Audit Log Information from the PolAdtLg attribute of the Lsa
+ // Database object.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[PolAdtLg],
+ &LsapAdtLogInformation,
+ &AuditLogInfoLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapAdtInitialize: Read Audit Log Info returned 0x%lx\n",
+ Status
+ );
+
+ goto AuditInitError;
+ }
+
+ //
+ // Query the Audit Log Full Information in the LSA Database. Note
+ // that it is too early to update a log full condition, so don't
+ // try to write to the Audit Log.
+ //
+
+ Status = LsapAdtQueryAuditLogFullInfo(
+ LsapDbHandle,
+ (ULONG) 0,
+ &LsapAdtLogFullInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapAdtInitialize: Update Audit Log Full Info returned 0x%lx\n",
+ Status
+ );
+
+ goto AuditInitError;
+ }
+
+ //
+ // Read the Audit Event Information from the AdtEvent attribute of the Lsa
+ // Database object. The information consists of the Auditing Mode and
+ // the Auditing Options for each Audit Event Type.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[PolAdtEv],
+ &AuditEventsInfo,
+ &AuditEventInfoLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // This section of code is temporary and allows an old
+ // Policy Database to work with the new Audit Event Categories
+ // without the need to re-install. The Audit Event Information
+ // is overwritten with the new format and all auditing is turned
+ // off.
+ //
+
+ if (Status == STATUS_BUFFER_OVERFLOW) {
+
+ KdPrint(("LsapAdtInitialize: Old Audit Event Info detected\n"
+ "Replacing with new format, all auditing disabled\n"));
+
+ //
+ // Initialize Default Event Auditing Options. No auditing is specified
+ // for any event type.
+ //
+
+ Status = LsapAdtInitializeDefaultAuditing(
+ LSAP_DB_UPDATE_POLICY_DATABASE,
+ &AuditEventsInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto AuditInitError;
+ }
+
+ } else {
+
+ LsapLogError(
+ "LsapAdtInitialize: Read Audit Event Info returned 0x%lx\n",
+ Status
+ );
+ goto AuditInitError;
+ }
+ }
+
+ //
+ // Set global flags to tell us if we're supposed to be auditing
+ // successful logons, failed logons, or both
+ //
+ //
+
+ LsapAdtAuditingLogon( &AuditEventsInfo );
+
+ //
+ // During system initialization, we are effectively logged on as
+ // system.
+ //
+
+ LsapAdtSystemRestart( &AuditEventsInfo );
+
+ (VOID) LsapAdtInitializeCrashOnFail();
+
+ //
+ // Send a command to the Reference Monitor to write the Auditing
+ // State to its in-memory data.
+ //
+
+ Status = LsapCallRm(
+ RmAuditSetCommand,
+ &AuditEventsInfo,
+ sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError("LsapAdtInitialize: LsapCallRm returned 0x%lx\n", Status);
+ goto AuditInitError;
+ }
+
+ RtlCopyMemory(
+ &LsapAdtEventsInformation,
+ &AuditEventsInfo,
+ sizeof(LSARM_POLICY_AUDIT_EVENTS_INFO)
+ );
+
+
+ LsapAdtInitializeDriveLetters();
+
+ //
+ // Initialize privilege values we need
+ //
+
+ ChangeNotifyPrivilege = RtlConvertLongToLuid( SE_CHANGE_NOTIFY_PRIVILEGE );
+ AuditPrivilege = RtlConvertLongToLuid( SE_AUDIT_PRIVILEGE );
+ CreateTokenPrivilege = RtlConvertLongToLuid( SE_CREATE_TOKEN_PRIVILEGE );
+ AssignPrimaryTokenPrivilege = RtlConvertLongToLuid( SE_ASSIGNPRIMARYTOKEN_PRIVILEGE );
+ BackupPrivilege = RtlConvertLongToLuid( SE_BACKUP_PRIVILEGE );
+ RestorePrivilege = RtlConvertLongToLuid( SE_RESTORE_PRIVILEGE );
+ DebugPrivilege = RtlConvertLongToLuid( SE_DEBUG_PRIVILEGE );
+
+ //
+ // Tell base/wincon how to shut us down.
+ // First, tell base to shut us down as late in the game as possible.
+ //
+
+ SetProcessShutdownParameters(LSAP_SHUTDOWN_LEVEL, SHUTDOWN_NORETRY);
+
+ // And, tell them what function to call when we are being shutdown:
+
+ SetConsoleCtrlHandler(LsapShutdownNotification, TRUE);
+
+
+ } else if (Pass == 2) {
+
+ //
+ // Write out any Audit Records that were cached during the
+ // first stage of initialization. The Audit Log will be opened
+ // on the first write if necessary.
+ //
+
+ //
+ // BUGBUG - ScottBi 8/6/92 - This action cannot be taken here
+ // unless we know that the EventLog service is running. For now,
+ // an attempt is made to open the log each time an Audit Record
+ // is generated, and the cache grows until a limit is reached,
+ // at which point auditing is turned off and subsequent records
+ // are discarded.
+ //
+
+ /*
+ Status = LsapAdtWriteLog( NULL, (ULONG) 0);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto AuditInitError;
+ }
+ */
+ }
+
+AuditInitFinish:
+
+ if (AcquiredLock) {
+
+ LsapDbReleaseLock();
+ }
+
+ return(Status);
+
+AuditInitError:
+
+ //
+ // If the Audit Log is full, signal the Log Full condition
+ //
+
+ if (Status == STATUS_LOG_FILE_FULL) {
+
+ SecondaryStatus = LsapAdtSignalLogFull();
+ }
+
+ //
+ // If auditing failed to initialize, output warning and disable
+ // auditing.
+ //
+
+ if (Pass == 1) {
+
+ LsapLogError(
+ "LSA: Warning - Audit Initialization Pass 1 Returned 0x%lx\n"
+ " Auditing has been disabled\n",
+ Status
+ );
+
+ } else {
+
+ LsapLogError(
+ "LSA: Warning - Audit Initialization Pass 2 Returned 0x%lx\n"
+ " Auditing has been disabled\n",
+ Status
+ );
+ }
+
+ LsapAdtEventsInformation.AuditingMode = FALSE;
+
+ Status = LsarSetInformationPolicy(
+ LsapDbHandle,
+ PolicyAuditEventsInformation,
+ (PLSAPR_POLICY_INFORMATION) &LsapAdtEventsInformation
+ );
+
+ goto AuditInitFinish;
+}
+
+
+NTSTATUS
+LsapAdtInitializeDefaultAuditing(
+ IN ULONG Options,
+ OUT PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets an initial default Auditing State in which auditing
+ is turned off. It is called only during initialization of the LSA
+ or during the installation of its Policy Database. The initial
+ auditing state may also optionally be written to the Lsa Policy
+ Database provided that the Policy Object has been created and its
+ internal handle is available.
+
+Arguments:
+
+ Options - Specifies optional actions to be taken
+
+ LSAP_DB_UPDATE_POLICY_DATABASE - Update the corresponding information
+ in the Policy Database. This option must only be specified
+ where it is known that the Policy Object exists.
+
+ AuditEventsInformation - Pointer to structure that will receive the Audit Event
+ Information
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_ATTRIBUTE AuditEventsAttribute;
+ BOOLEAN ObjectReferenced = FALSE;
+
+ ULONG EventAuditingOptionsLength =
+ (POLICY_AUDIT_EVENT_TYPE_COUNT * sizeof(POLICY_AUDIT_EVENT_OPTIONS));
+
+ //
+ // Turn off auditing and set the count of Audit Event Types (Categories)
+ //
+
+ AuditEventsInformation->AuditingMode = FALSE;
+ AuditEventsInformation->MaximumAuditEventCount = POLICY_AUDIT_EVENT_TYPE_COUNT;
+
+ //
+ // Turn off auditing for all events.
+ //
+
+ RtlZeroMemory(AuditEventsInformation->EventAuditingOptions, EventAuditingOptionsLength);
+
+
+ if (Options & LSAP_DB_UPDATE_POLICY_DATABASE) {
+
+ ASSERT(LsapPolicyHandle != NULL);
+
+ //
+ // Start a transaction on the Policy Object
+ //
+
+ Status = LsapDbReferenceObject(
+ LsapPolicyHandle,
+ (ACCESS_MASK) 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeDefaultAuditingError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ LsapDbInitializeAttribute(
+ &AuditEventsAttribute,
+ &LsapDbNames[PolAdtEv],
+ AuditEventsInformation,
+ sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
+ FALSE
+ );
+
+ Status = LsapDbWriteAttributesObject(
+ LsapPolicyHandle,
+ &AuditEventsAttribute,
+ (ULONG) 1
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeDefaultAuditingError;
+ }
+ }
+
+InitializeDefaultAuditingFinish:
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ LsapPolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ ObjectReferenced = FALSE;
+ }
+
+ return(Status);
+
+InitializeDefaultAuditingError:
+
+ goto InitializeDefaultAuditingFinish;
+}
+
+
+NTSTATUS
+LsapAdtInitializeLogQueue(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the Audit Log Queue.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Currently, STATUS_SUCCESS is always returned.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ Status = RtlInitializeCriticalSection(&LsapAdtQueueLock);
+
+ if (NT_SUCCESS(Status)) {
+ Status = RtlInitializeCriticalSection(&LsapAdtLogFullLock);
+ }
+
+ LsapAdtLogQueue.FirstQueuedRecord = NULL;
+ LsapAdtLogQueue.LastQueuedRecord = NULL;
+
+ return(Status);
+}
+
+
+
+
+
+
+VOID
+LsapAdtAuditingLogon(
+ PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Examines auditing data and determines if we are auditing
+ logon events.
+
+Arguments:
+
+
+ AuditEventsInfo - Auditing data.
+
+Return Value:
+
+ TRUE if auditing logon, FALSE otherwise.
+
+
+--*/
+
+{
+
+ if ( !AuditEventsInfo->AuditingMode ) {
+
+ LsapAuditSuccessfulLogons = FALSE;
+ LsapAuditFailedLogons = FALSE;
+
+ return;
+ }
+
+ if ( (AuditEventsInfo->EventAuditingOptions)[AuditCategoryLogon] & POLICY_AUDIT_EVENT_SUCCESS ) {
+
+ LsapAuditSuccessfulLogons = TRUE;
+
+ } else {
+
+ LsapAuditSuccessfulLogons = FALSE;
+ }
+
+
+
+
+ if ( (AuditEventsInfo->EventAuditingOptions)[AuditCategoryLogon] & POLICY_AUDIT_EVENT_FAILURE ) {
+
+ LsapAuditFailedLogons = TRUE;
+
+ } else {
+
+ LsapAuditFailedLogons = FALSE;
+ }
+}
+
+
+
+VOID
+LsapAdtInitializeDriveLetters(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initializes an array of symbolic link to drive letter mappings
+ for use by auditing code.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNICODE_STRING LinkName;
+ PUNICODE_STRING DeviceName;
+ OBJECT_ATTRIBUTES Obja;
+ HANDLE LinkHandle;
+ NTSTATUS Status;
+ ULONG i;
+ PWCHAR p;
+ PWCHAR DeviceNameBuffer;
+ ULONG MappingIndex = 0;
+
+ WCHAR wszDosDevices[sizeof(L"\\DosDevices\\A:") + 1];
+
+ wcscpy(wszDosDevices, L"\\DosDevices\\A:");
+
+ RtlInitUnicodeString(&LinkName, wszDosDevices);
+
+
+ p = (PWCHAR)LinkName.Buffer;
+
+ //
+ // Make p point to the drive letter in the LinkName string
+ //
+
+ p = p+12;
+
+
+
+ for( i=0 ; i<26 ; i++ ){
+
+ *p = (WCHAR)'A' + (WCHAR)i;
+
+ InitializeObjectAttributes(
+ &Obja,
+ &LinkName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ Status = NtOpenSymbolicLinkObject(
+ &LinkHandle,
+ SYMBOLIC_LINK_QUERY,
+ &Obja
+ );
+ if (NT_SUCCESS( Status )) {
+
+ //
+ // Open succeeded, Now get the link value
+ //
+
+ DriveMappingArray[MappingIndex].DriveLetter = *p;
+ DeviceName = &DriveMappingArray[MappingIndex].DeviceName;
+
+
+ DeviceNameBuffer = LsapAllocateLsaHeap( MAXIMUM_FILENAME_LENGTH );
+
+
+ DeviceName->Length = 0;
+ DeviceName->MaximumLength = MAXIMUM_FILENAME_LENGTH;
+ DeviceName->Buffer = DeviceNameBuffer;
+
+ Status = NtQuerySymbolicLinkObject(
+ LinkHandle,
+ DeviceName,
+ NULL
+ );
+
+ NtClose(LinkHandle);
+
+ if ( NT_SUCCESS(Status) ) {
+
+ MappingIndex++;
+
+ } else {
+
+ LsapFreeLsaHeap( DeviceNameBuffer );
+ RtlInitUnicodeString( DeviceName, NULL );
+ }
+ }
+ }
+}
+
+
+NTSTATUS
+LsapAdtInitializeCrashOnFail(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to see if the user has told us to crash if an audit fails.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ HANDLE KeyHandle;
+ NTSTATUS Status;
+ NTSTATUS TmpStatus;
+ OBJECT_ATTRIBUTES Obja;
+ ULONG ResultLength;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING ValueName;
+ CHAR KeyInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BOOLEAN)];
+ PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
+
+ //
+ // Check the value of the CrashOnAudit key.
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
+
+ InitializeObjectAttributes( &Obja,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenKey(
+ &KeyHandle,
+ KEY_QUERY_VALUE | KEY_SET_VALUE,
+ &Obja
+ );
+
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ LsapCrashOnAuditFail = FALSE;
+ return( STATUS_SUCCESS );
+ }
+
+ RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
+
+ Status = NtQueryValueKey(
+ KeyHandle,
+ &ValueName,
+ KeyValuePartialInformation,
+ KeyInfo,
+ sizeof(KeyInfo),
+ &ResultLength
+ );
+
+ TmpStatus = NtClose(KeyHandle);
+ ASSERT(NT_SUCCESS(TmpStatus));
+
+ //
+ // If it's not found, don't enable CrashOnFail.
+ //
+
+ if (!NT_SUCCESS( Status )) {
+
+ LsapCrashOnAuditFail = FALSE;
+
+ } else {
+
+ //
+ // Check the value of the CrashOnFail value. If it is 1, we
+ // crash on audit fail. If it is two, we only allow admins to
+ // logon.
+ //
+
+ pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;
+ if (*(pKeyInfo->Data) == LSAP_CRASH_ON_AUDIT_FAIL) {
+ LsapCrashOnAuditFail = TRUE;
+ } else if (*(pKeyInfo->Data) == LSAP_ALLOW_ADIMIN_LOGONS_ONLY) {
+ LsapAllowAdminLogonsOnly = TRUE;
+ }
+
+ }
+
+ if ( LsapCrashOnAuditFail ) {
+
+ BOOLEAN WasEnabled;
+
+ Status = RtlAdjustPrivilege(
+ SE_SHUTDOWN_PRIVILEGE,
+ TRUE,
+ FALSE,
+ &WasEnabled
+ );
+
+ //
+ // This had better work.
+ //
+
+ ASSERT(NT_SUCCESS(Status));
+
+ return( Status );
+
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+BOOL
+LsapShutdownNotification(
+ IN ULONG ControlType
+ )
+/*++
+
+Routine Description:
+
+ This routine sets a global flag indicating that shutdown is in progress.
+ The flag is used by the auditing subsystem to tell whether to bugcheck
+ when an audit fails - we don't want to bugcheck during shutdown because
+ the eventlog stopped.
+
+Arguments:
+
+ ControlType - a flag indicating what event occurred.
+
+Return Value:
+
+ TRUE
+
+--*/
+{
+ if (ControlType == CTRL_SHUTDOWN_EVENT) {
+ LsapShutdownInProgress = TRUE;
+ }
+ return(TRUE);
+}
+
diff --git a/private/lsa/server/adtlog.c b/private/lsa/server/adtlog.c
new file mode 100644
index 000000000..702ee570d
--- /dev/null
+++ b/private/lsa/server/adtlog.c
@@ -0,0 +1,3687 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtlog.c
+
+Abstract:
+
+ Local Security Authority - Audit Log Management
+
+ Functions in this module access the Audit Log via the Event Logging
+ interface.
+
+Author:
+
+ Scott Birrell (ScottBi) November 20, 1991
+ Robert Reichel (RobertRe) April 4, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <msaudite.h>
+#include "lsasrvp.h"
+#include "ausrvp.h"
+#include "adtp.h"
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Private data for Audit Logs and Events //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Audit Events Information.
+//
+
+LSARM_POLICY_AUDIT_EVENTS_INFO LsapAdtEventsInformation;
+
+//
+// Audit Log Information. This must be kept in sync with the information
+// in the Lsa Database.
+//
+
+POLICY_AUDIT_LOG_INFO LsapAdtLogInformation;
+
+//
+// Audit Log Full Information.
+//
+
+POLICY_AUDIT_FULL_QUERY_INFO LsapAdtLogFullInformation;
+
+//
+// Audit Log Handle (returned by Event Logger).
+//
+
+HANDLE LsapAdtLogHandle = NULL;
+
+//
+// Audit Log Full Event Handle
+//
+
+HANDLE LsapAdtLogFullEventHandle;
+
+//
+// Lsa Global flagS to indicate if we are auditing logon events.
+//
+
+BOOLEAN LsapAuditSuccessfulLogons = FALSE;
+BOOLEAN LsapAuditFailedLogons = FALSE;
+
+//
+// Flag to tell us if we're supposed to crash when an audit fails.
+//
+
+BOOLEAN LsapAdtCrashOnAuditFail = FALSE;
+
+RTL_CRITICAL_SECTION LsapAdtQueueLock;
+RTL_CRITICAL_SECTION LsapAdtLogFullLock;
+BOOLEAN LsapAdtSignalFullInProgress;
+
+LSAP_ADT_LOG_QUEUE_HEAD LsapAdtLogQueue;
+
+//
+// Maintain the length of the audit queue.
+// If it gets too long, start discarding audits
+// so we don't suck up all of memory
+//
+
+ULONG LsapAuditQueueLength = 0;
+ULONG LsapAuditQueueEventsDiscarded = 0;
+
+#define MAX_AUDIT_QUEUE_LENGTH 500
+
+//
+// Private prototypes
+//
+
+VOID
+LsapAdtAuditDiscardedAudits(
+ ULONG NumberOfEventsDiscarded
+ );
+
+//////////////////////////////////////////////////////////
+
+NTSTATUS
+LsapAdtWriteLogWrkr(
+ IN PLSA_COMMAND_MESSAGE CommandMessage,
+ OUT PLSA_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function handles a command, received from the Reference Monitor via
+ the LPC link, to write a record to the Audit Log. It is a wrapper which
+ deals with any LPC unmarshalling.
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing LSA command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (LsapWriteAuditMessageCommand). This command
+ contains an Audit Message Packet (TBS) as a parameter.
+
+ ReplyMessage - Pointer to structure containing LSA reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ Currently, all other errors from called routines are suppressed.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG RegionSize = 0;
+
+ PSE_ADT_PARAMETER_ARRAY AuditRecord = NULL;
+
+ //
+ // Strict check that command is correct.
+ //
+
+ ASSERT( CommandMessage->CommandNumber == LsapWriteAuditMessageCommand );
+
+ //
+ // Obtain a pointer to the Audit Record. The Audit Record is
+ // either stored as immediate data within the Command Message,
+ // or it is stored as a buffer. In the former case, the Audit Record
+ // begins at CommandMessage->CommandParams and in the latter case,
+ // it is stored at the address located at CommandMessage->CommandParams.
+ //
+
+ if (CommandMessage->CommandParamsMemoryType == SepRmImmediateMemory) {
+
+ AuditRecord = (PSE_ADT_PARAMETER_ARRAY) CommandMessage->CommandParams;
+
+ } else {
+
+ AuditRecord = *((PSE_ADT_PARAMETER_ARRAY *) CommandMessage->CommandParams);
+ }
+
+ //
+ // Call worker to queue Audit Record for writing to the log.
+ //
+
+ Status = LsapAdtWriteLog( AuditRecord, (ULONG) 0 );
+
+ UNREFERENCED_PARAMETER(ReplyMessage); // Intentionally not referenced
+
+ //
+ // The status value returned from LsapAdtWriteLog() is intentionally
+ // ignored, since there is no meaningful action that the client
+ // (i.e. kernel) if this LPC call can take. If an error occurs in
+ // trying to append an Audit Record to the log, the LSA handles the
+ // error.
+ //
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+LsapAdtOpenLog(
+ OUT PHANDLE AuditLogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens the Audit Log.
+
+Arguments:
+
+ AuditLogHandle - Receives the Handle to the Audit Log.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ All result codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ UNICODE_STRING ModuleName;
+
+ RtlInitUnicodeString( &ModuleName, L"Security");
+
+ Status = ElfRegisterEventSourceW (
+ NULL,
+ &ModuleName,
+ AuditLogHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenLogError;
+ }
+
+
+OpenLogFinish:
+
+ return(Status);
+
+OpenLogError:
+
+ //
+ // Check for Log Full and signal the condition.
+ //
+
+ if (Status != STATUS_LOG_FILE_FULL) {
+
+ goto OpenLogFinish;
+ }
+
+ //
+ // The log is full. Deal with this condition according to
+ // the local policy options in effect.
+ //
+
+ SecondaryStatus = LsapAdtSignalLogFull();
+
+ goto OpenLogFinish;
+}
+
+
+NTSTATUS
+LsapAdtQueueRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ Puts passed audit record on the queue to be logged.
+
+ This routine will convert the passed AuditParameters structure
+ into self-relative form if it is not already. It will then
+ allocate a buffer out of the local heap and copy the audit
+ information into the buffer and put it on the audit queue.
+
+ The buffer will be freed when the queue is cleared.
+
+Arguments:
+
+ AuditRecord - Contains the information to be audited.
+
+ Options - Speciifies optional actions to be taken
+
+ LSAP_ADT_LOG_QUEUE_PREPEND - Put record on front of queue. If
+ not specified, the record will be appended to the queue.
+ This option is specified when a special audit record of the
+ type AuditEventLogNoLongerFull is generated, so that the
+ record will be written out before others in the queue. The
+ presence of a record of this type in the log indicates that
+ one or more preceding Audit Records may have been lost
+ tdue to the log filling up.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to allocate a buffer to contain the record.
+--*/
+
+{
+ ULONG AuditRecordLength;
+ PLSAP_ADT_QUEUED_RECORD QueuedAuditRecord;
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG AllocationSize;
+ PSE_ADT_PARAMETER_ARRAY MarshalledAuditParameters;
+ BOOLEAN AcquiredLock = FALSE;
+ BOOLEAN FreeWhenDone = FALSE;
+
+ //
+ // Check to see if the list is above the maximum length.
+ // If it gets this high, it is more than likely that the
+ // eventlog service is not going to start at all, so
+ // start tossing audits.
+ //
+ // Don't do this if crash on audit is set.
+ //
+
+ if ((LsapAuditQueueLength > MAX_AUDIT_QUEUE_LENGTH) && !LsapCrashOnAuditFail) {
+ LsapAuditQueueEventsDiscarded++;
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Gather up all of the passed information into a single
+ // block that can be placed on the queue.
+ //
+
+ if ( AuditParameters->Flags & SE_ADT_PARAMETERS_SELF_RELATIVE ) {
+
+ MarshalledAuditParameters = AuditParameters;
+
+ } else {
+
+ Status = LsapAdtMarshallAuditRecord(
+ AuditParameters,
+ &MarshalledAuditParameters
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ goto QueueAuditRecordError;
+
+ } else {
+
+ //
+ // Indicate that we're to free this structure when we're
+ // finished
+ //
+
+ FreeWhenDone = TRUE;
+ }
+ }
+
+ Status = LsapAdtAcquireLogQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueueAuditRecordError;
+ }
+
+ AcquiredLock = TRUE;
+
+ //
+ // Copy the now self-relative audit record into a buffer
+ // that can be placed on the queue.
+ //
+
+ AuditRecordLength = MarshalledAuditParameters->Length;
+ AllocationSize = AuditRecordLength + sizeof( PLSAP_ADT_QUEUED_RECORD );
+
+ QueuedAuditRecord = (PLSAP_ADT_QUEUED_RECORD)LsapAllocateLsaHeap( AllocationSize );
+
+ if ( QueuedAuditRecord == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto QueueAuditRecordError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ RtlCopyMemory( &QueuedAuditRecord->Buffer, MarshalledAuditParameters, AuditRecordLength );
+
+ //
+ // We are finished with the marshalled audit record, free it.
+ //
+
+ if ( FreeWhenDone ) {
+ LsapFreeLsaHeap( MarshalledAuditParameters );
+ FreeWhenDone = FALSE;
+ }
+
+ if (!(Options & LSAP_ADT_LOG_QUEUE_PREPEND)) {
+
+ //
+ // Append the new record to the back of the list. Update the
+ // pointer to the last record and, if the queue was empty, update
+ // the pointer to the first record.
+ //
+
+ if (LsapAdtLogQueue.LastQueuedRecord != NULL) {
+
+ LsapAdtLogQueue.LastQueuedRecord->Next = QueuedAuditRecord;
+
+ } else {
+
+ ASSERT( LsapAdtLogQueue.FirstQueuedRecord == NULL );
+ LsapAdtLogQueue.FirstQueuedRecord = QueuedAuditRecord;
+ }
+
+ LsapAdtLogQueue.LastQueuedRecord = QueuedAuditRecord;
+ QueuedAuditRecord->Next = NULL;
+
+ } else {
+
+ //
+ // Record is to be prepended to queue.
+ //
+
+ QueuedAuditRecord->Next = LsapAdtLogQueue.FirstQueuedRecord;
+ LsapAdtLogQueue.FirstQueuedRecord = QueuedAuditRecord;
+
+ if (LsapAdtLogQueue.LastQueuedRecord == NULL) {
+
+ LsapAdtLogQueue.LastQueuedRecord = QueuedAuditRecord;
+ }
+ }
+
+ LsapAuditQueueLength++;
+
+QueueAuditRecordFinish:
+
+ //
+ // If necessary, release the Audit Queue Lock.
+ //
+
+ if (AcquiredLock) {
+
+ LsapAdtReleaseLogQueueLock();
+ }
+
+ return(Status);
+
+QueueAuditRecordError:
+
+ if ( FreeWhenDone ) {
+ LsapFreeLsaHeap( MarshalledAuditParameters );
+ }
+
+ goto QueueAuditRecordFinish;
+}
+
+
+VOID
+LsapAdtAuditLogon(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ PUNICODE_STRING AccountName,
+ PUNICODE_STRING AuthenticatingAuthority,
+ PUNICODE_STRING Source,
+ PUNICODE_STRING SourceDevice,
+ PUNICODE_STRING PackageName,
+ SECURITY_LOGON_TYPE LogonType,
+ PSID UserSid,
+ LUID AuthenticationId,
+ NTSTATUS LogonStatus,
+ PUNICODE_STRING WorkstationName
+ )
+
+/*++
+
+Routine Description:
+
+ Generates an audit of a logon event as appropriate.
+
+Arguments:
+
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING SpareString;
+ UNICODE_STRING AuthenticationIdString;
+ BOOLEAN FreeWhenDone = FALSE;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ BOOLEAN AuditingSuccess;
+ BOOLEAN AuditingFailure;
+ PSID LocalSystemSid = NULL;
+ UNICODE_STRING NullString;
+
+ RtlInitUnicodeString( &NullString, L"" );
+
+ RtlInitUnicodeString( &SpareString, L"Security");
+
+ AuditingFailure = (EventType == EVENTLOG_AUDIT_FAILURE) && LsapAuditFailedLogons;
+ AuditingSuccess = (EventType == EVENTLOG_AUDIT_SUCCESS) && LsapAuditSuccessfulLogons;
+
+ if ( AuditingFailure || AuditingSuccess ) {
+
+ //
+ // Build an audit parameters structure.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = EventCategory;
+ AuditParameters.AuditId = EventID;
+ AuditParameters.Type = EventType;
+ AuditParameters.ParameterCount = 0;
+
+ //
+ // User Sid
+ //
+
+ if ( AuditingSuccess ) {
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+
+ } else {
+
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &LocalSystemSid
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ LsapAuditFailed();
+ goto Finish;
+
+ } else {
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
+ }
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Subsystem name (if available)
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SpareString );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Account name
+ //
+
+ if (ARGUMENT_PRESENT(AccountName)) {
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AccountName );
+ } else {
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &NullString );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Authenticating Authority (domain name)
+ //
+
+ if (ARGUMENT_PRESENT(AuthenticatingAuthority)) {
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AuthenticatingAuthority );
+ } else {
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &NullString );
+
+ }
+ AuditParameters.ParameterCount++;
+
+ if ( AuditingSuccess ) {
+
+ //
+ // Logon Id (as a string)
+ //
+
+ Status = LsapAdtBuildLuidString(
+ &AuthenticationId,
+ &AuthenticationIdString,
+ &FreeWhenDone
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &AuthenticationIdString );
+
+ } else {
+
+ LsapAuditFailed();
+ goto Finish;
+ }
+
+ AuditParameters.ParameterCount++;
+ }
+
+ //
+ // Logon Type
+ //
+
+ LsapSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, LogonType );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Source
+ //
+
+ if ( ARGUMENT_PRESENT( Source )) {
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, Source );
+
+ } else {
+
+ //
+ // No need to do anything here, since an empty entry will turn
+ // into a '-' in the output
+ //
+
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Authentication Package
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, PackageName );
+ AuditParameters.ParameterCount++;
+
+ //
+ // Authentication Package
+ //
+
+ if ( ARGUMENT_PRESENT( WorkstationName )) {
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, WorkstationName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+Finish:
+
+ if ( LocalSystemSid != NULL ) {
+ LsapFreeLsaHeap( LocalSystemSid );
+ }
+
+ if ( FreeWhenDone ) {
+ LsapFreeLsaHeap( AuthenticationIdString.Buffer );
+ }
+ }
+}
+
+
+VOID
+LsapAdtAuditLogoff(
+ PLSAP_LOGON_SESSION Session
+ )
+/*++
+
+Routine Description:
+
+ Generates a logoff audit. The caller is responsible for determining
+ if logoff auditing is necessary.
+
+Arguments:
+
+ Session - Points to the logon session being removed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = SE_CATEGID_LOGON;
+ AuditParameters.AuditId = SE_AUDITID_LOGOFF;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+ AuditParameters.ParameterCount = 0;
+
+ //
+ // User Sid
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, Session->UserSid );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Subsystem name
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Logon ID
+ //
+
+ LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, Session->LogonId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Logon Type
+ //
+
+ LsapSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, Session->LogonType );
+
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+}
+
+
+VOID
+LsapAdtSystemRestart(
+ PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called during LSA initialization to generate
+ a system restart event.
+
+Arguments:
+
+ AuditEventsInfo - Auditing data.
+
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+
+ if(!AuditEventsInfo->AuditingMode) {
+
+ return;
+ }
+
+ if (!((AuditEventsInfo->EventAuditingOptions)[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS )) {
+
+ return;
+ }
+
+ //
+ // Construct an audit parameters array
+ // for the restart event.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
+ AuditParameters.AuditId = SE_AUDITID_SYSTEM_RESTART;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+ AuditParameters.ParameterCount = 0;
+
+ //
+ // User Sid
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LsapLocalSystemSid );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Subsystem name (if available)
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ return;
+}
+
+
+
+
+
+
+NTSTATUS
+LsapAdtWriteLog(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters OPTIONAL,
+ IN ULONG Options
+ )
+/*++
+
+Routine Description:
+
+ This function appends an Audit Record and/or the content of the
+ Audit Record Log Queue to the Audit Log, by calling the Event Logger.
+ If the Audit Log becomes full, this function signals an Audit Log
+ Full condition. The Audit Log will be opened if necessary.
+
+ NOTE: This function may be called during initialization before
+ the Event Logger service has started. In that event, any Audit
+ Record specified will simply be added to the queue.
+
+Arguments:
+
+ AuditRecord - Optional pointer to an Audit Record to be written to
+ the Audit Log. The record will first be added to the existing queue
+ of records waiting to be written to the log. An attempt will then
+ be made to write all of the records in the queue to the log. If
+ NULL is specified, the existing queue will be written out.
+
+ Options - Specifies optional actions to be taken.
+
+ LSAP_ADT_LOG_QUEUE_PREPEND - Prepend record to the Audit Record
+ queue prior to writing to the log. If not specified, the
+ record will be appended to the queue.
+
+ LSAP_ADT_LOG_QUEUE_DISCARD - Discard the Audit Record Queue.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ BOOLEAN AcquiredLock = FALSE;
+ BOOLEAN AuditRecordFreed = FALSE;
+ BOOLEAN AuditRecordUnblocked = FALSE;
+ BOOLEAN ShutdownSystem = FALSE;
+ NTSTATUS Status;
+ NTSTATUS SecondaryStatus;
+ PLSAP_ADT_QUEUED_RECORD Front, Back;
+
+
+ SecondaryStatus = STATUS_SUCCESS;
+
+
+// //
+// // If we have not reached Pass 2 Audit Initialization, we need to
+// // cache the audit record.
+// //
+//
+// if (LsapAdtInitializationPass < 2) {
+//
+// goto WriteLogFinish;
+// }
+
+
+ if ( Options & LSAP_ADT_LOG_QUEUE_DISCARD ) {
+
+ //
+ // Flush out the queue, if there is one.
+ //
+
+ Front = LsapAdtLogQueue.FirstQueuedRecord;
+ Back = NULL;
+
+ //
+ // Free the records in the queue.
+ //
+
+ while ( Front != NULL ) {
+
+ Back = Front;
+ Front = Front->Next;
+
+ LsapFreeLsaHeap( Back );
+ LsapAuditQueueLength--;
+ }
+
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // If the Audit Log is not already open, attempt to open it.
+ // If this open is unsuccessful because the EventLog service has not
+ // started, queue the Audit Record if directed to do so
+ // via the Options parameter. If the open is unsuccessful for any
+ // other reason, discard the Audit Record.
+ //
+
+ if ( LsapAdtLogHandle == NULL ) {
+
+ if (ARGUMENT_PRESENT( AuditParameters )) {
+
+ Status = LsapAdtQueueRecord( AuditParameters, 0 );
+
+ if (!NT_SUCCESS( Status )) {
+ goto WriteLogError;
+ }
+ }
+
+ Status = LsapAdtOpenLog(&LsapAdtLogHandle);
+
+ if (!NT_SUCCESS(Status)) {
+ goto WriteLogFinish;
+
+ } else {
+
+ //
+ // Prepare to write out all of the records in the Audit Log Queue.
+ // First, we need to capture the existing queue.
+ //
+
+ Status = LsapAdtAcquireLogQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+ goto WriteLogError;
+ }
+
+ AcquiredLock = TRUE;
+
+ Front = LsapAdtLogQueue.FirstQueuedRecord;
+ Back = NULL;
+
+ //
+ // Write out records in the queue.
+ //
+
+ while ( Front != NULL ) {
+
+ AuditParameters = &Front->Buffer;
+
+ LsapAdtNormalizeAuditInfo( AuditParameters );
+
+ Status = LsapAdtDemarshallAuditInfo(
+ AuditParameters
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ break;
+ }
+
+ //
+ // Update the Audit Log Information in the Policy Object. We
+ // increment the Next Audit Record serial number.
+ //
+
+ if (LsapAdtLogInformation.NextAuditRecordId == LSAP_ADT_MAXIMUM_RECORD_ID ) {
+
+ LsapAdtLogInformation.NextAuditRecordId = 0;
+ }
+
+ LsapAdtLogInformation.NextAuditRecordId++;
+
+ Back = Front;
+ Front = Front->Next;
+
+ LsapFreeLsaHeap( Back );
+ LsapAuditQueueLength--;
+ }
+
+ if (LsapAuditQueueEventsDiscarded > 0) {
+
+ //
+ // We discarded some audits. Generate an audit
+ // so the user knows.
+ //
+
+ LsapAdtAuditDiscardedAudits( LsapAuditQueueEventsDiscarded );
+ }
+
+ //
+ // Make sure we don't ever try to do this again. Note that there
+ // is no point keeping the remainder of the queue if the Audit Log
+ // has become full, because a reboot is needed to clear the full
+ // condition.
+ //
+
+ LsapAdtLogQueue.FirstQueuedRecord = NULL;
+ LsapAdtLogQueue.LastQueuedRecord = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+ goto WriteLogError;
+ }
+ }
+
+ } else {
+
+ //
+ // Normal case, just perform the audit
+ //
+
+ LsapAdtNormalizeAuditInfo( AuditParameters );
+
+ Status = LsapAdtDemarshallAuditInfo(
+ AuditParameters
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto WriteLogError;
+ }
+
+ //
+ // Update the Audit Log Information in the Policy Object. We
+ // increment the Next Audit Record serial number.
+ //
+
+ if (LsapAdtLogInformation.NextAuditRecordId == LSAP_ADT_MAXIMUM_RECORD_ID ) {
+
+ LsapAdtLogInformation.NextAuditRecordId = 0;
+ }
+
+ LsapAdtLogInformation.NextAuditRecordId++;
+
+ }
+
+
+WriteLogFinish:
+
+ //
+ // If necessary, release the LSA Audit Log Queue Lock.
+ //
+
+ if (AcquiredLock) {
+
+ LsapAdtReleaseLogQueueLock();
+ AcquiredLock = FALSE;
+ }
+
+ return(Status);
+
+WriteLogError:
+
+ //
+ // Take whatever action we're supposed to take when an audit attempt fails.
+ //
+
+ LsapAuditFailed();
+
+ //
+ // If the error is other than Audit Log Full, just cleanup and return
+ // the error.
+ //
+
+ if ((Status != STATUS_DISK_FULL) && (Status != STATUS_LOG_FILE_FULL)) {
+
+ goto WriteLogFinish;
+ }
+
+ //
+ // The Audit Log is full. Deal with this condition according to
+ // local policy in effect.
+ //
+
+ SecondaryStatus = LsapAdtSignalLogFull();
+
+ //
+ // If there are Audit Records in the cache, discard them.
+ //
+
+ SecondaryStatus = LsapAdtWriteLog(NULL, LSAP_ADT_LOG_QUEUE_DISCARD);
+
+ if (NT_SUCCESS(Status)) {
+ Status = SecondaryStatus;
+ }
+
+ goto WriteLogFinish;
+}
+
+
+
+
+
+NTSTATUS
+LsarClearAuditLog(
+ IN LSAPR_HANDLE PolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function used to clear the Audit Log but has been superseded
+ by the Event Viewer functionality provided for this purpose. To
+ preserve compatibility with existing RPC interfaces, this server
+ stub is retained.
+
+Arguments:
+
+ PolicyHandle - Handle to an open Policy Object.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_NOT_IMPLEMENTED - This routine is not implemented.
+--*/
+
+{
+ UNREFERENCED_PARAMETER( PolicyHandle );
+ return(STATUS_NOT_IMPLEMENTED);
+}
+
+
+#if 0
+
+NTSTATUS
+LsapAdtSetInfoLog(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PPOLICY_AUDIT_LOG_INFO PolicyAuditLogInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This function updates the Audit Log Information, such as the size
+ of the Audit Log, Retention Period for Audit Records. The update
+ is done in three places, LSA's global data, the PolAdtLg attribute
+ of the Policy Object and the MaxSize and RetentionPeriod values of
+ the Audit Log's Registry Key.
+
+ If the Audit Log Full state changes from "full" to "not-full", the
+ Audit Record Cache will be emptied if necessary.
+
+ WARNING! The LSA Database must be locked before calling this function
+ and a transaction must be open.
+
+ NOTE: To change the characteristics of the Audit Log it is currently
+ necessary to write the values of its Registry Key directly,
+ because no Elf API is provided to do this.
+
+Arguments:
+
+ PolicyHandle - Handle to the Policy Object. This handle must already
+ have been validated. It must either be trusted, or must specify
+ POLICY_AUDIT_LOG_ADMIN access.
+
+ PolicyAuditLogInfo - Pointer to Audit Log Information.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status;
+ HANDLE AuditLogRegistryHandle = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ULONG Type = REG_DWORD;
+ TIME OldRetentionPeriod = LsapAdtLogInformation.AuditRetentionPeriod;
+
+
+ BOOLEAN LogWasFull = LsapAdtLogFullInformation.LogIsFull;
+
+ ASSERT( LsapDbIsLocked());
+
+ Status = LsapDbWriteAttributeObject(
+ PolicyHandle,
+ &LsapDbNames[PolAdtLg],
+ PolicyAuditLogInfo,
+ sizeof(POLICY_AUDIT_LOG_INFO)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoLogError;
+ }
+
+ RtlMoveMemory(
+ &LsapAdtLogInformation,
+ PolicyAuditLogInfo,
+ sizeof( POLICY_AUDIT_LOG_INFO )
+ );
+
+ //
+ // Now open the Audit Log's Registry Key using the absolute path of its
+ // Registry Key.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &LsapDbNames[AuditLog],
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenKey( &AuditLogRegistryHandle, KEY_WRITE, &ObjectAttributes );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoLogError;
+ }
+
+ //
+ // Now set the Audit Record Retention Period.
+ //
+
+ Status = NtSetValueKey(
+ AuditLogRegistryHandle,
+ &LsapDbNames[AuditRecordRetentionPeriod],
+ (ULONG) 0,
+ Type,
+ &PolicyAuditLogInfo->AuditRetentionPeriod,
+ sizeof( ULONG )
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoLogError;
+ }
+
+ //
+ // If the new Retention Period is 0, the Audit Log can be marked
+ // as no longer being full, since the Event Logger will wrap
+ // indefinitely.
+ //
+
+ if ( PolicyAuditLogInfo->AuditRetentionPeriod.QuadPart == 0) {
+
+ if (LsapAdtLogFullInformation.LogIsFull) {
+
+ LsapAdtLogFullInformation.LogIsFull = FALSE;
+
+ Status = LsapDbWriteAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[PolAdtFL],
+ &LsapAdtLogFullInformation,
+ sizeof(POLICY_AUDIT_FULL_QUERY_INFO)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoLogError;
+ }
+ }
+ }
+
+ //
+ // Now set the Audit Log Maximum Size MaxSize.
+ //
+
+ Status = NtSetValueKey(
+ AuditLogRegistryHandle,
+ &LsapDbNames[AuditLogMaxSize],
+ 0,
+ Type,
+ &PolicyAuditLogInfo->MaximumLogSize,
+ sizeof( ULONG )
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoLogError;
+ }
+
+ //
+ // Update the LSA's in-memory copy of the Audit Log Information.
+ //
+
+ RtlMoveMemory(
+ &LsapAdtLogInformation,
+ PolicyAuditLogInfo,
+ sizeof( POLICY_AUDIT_LOG_INFO )
+ );
+
+ //
+ // If the Audit Log has changed from "full" to "not full", clear the
+ // Audit Record Cache.
+ //
+
+ if (LogWasFull && !LsapAdtLogFullInformation.LogIsFull) {
+
+ Status = LsapAdtLogQueuedEvents((ULONG) 0);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoLogError;
+ }
+ }
+
+SetInfoLogFinish:
+
+ //
+ // If necessary, close the Audit Log Registry Key.
+ //
+
+ if (AuditLogRegistryHandle != NULL) {
+
+ Status = NtClose( AuditLogRegistryHandle );
+
+ AuditLogRegistryHandle = NULL;
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto SetInfoLogError;
+ }
+ }
+
+ return(Status);
+
+SetInfoLogError:
+
+ goto SetInfoLogFinish;
+}
+
+#endif
+
+
+
+
+NTSTATUS
+LsapAdtQueryAuditLogFullInfo(
+ IN PLSAPR_HANDLE PolicyHandle,
+ IN ULONG Options,
+ OUT PPOLICY_AUDIT_FULL_QUERY_INFO PolicyAuditFullQueryInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This function queries the Audit Log Full status as known to the LSA
+ Database. If this status is "Log Full", an attempt is optionally made
+ to check that it is really full by trying to write a special type
+ of Audit Record to the Audit Log.
+
+Arguments:
+
+ PolicyHandle - Handle to Policy Object with POLICY_AUDIT_LOG_ADMIN
+ and POLICY_SET_AUDIT_REQUIREMENTS access.
+
+ Options - Optional actions to be taken
+
+ LSAP_ADT_LOG_FULL_UPDATE - If the Audit Log Full information
+ specifies that the log is full, attempt to update this
+ information by actually trying to write a special type of
+ Audit Record to the Audit Log. If the write is successful,
+ update the Audit Log Full information to specify that the log
+ is no longer full.
+
+
+ PolicyAuditFullQueryInfo - Pointer to structure which will receive the
+ updated Audit Log Full information.
+--*/
+
+{
+ return( STATUS_SUCCESS );
+
+// NTSTATUS Status = STATUS_SUCCESS;
+// BOOLEAN AuditLogOpened = FALSE;
+// BOOLEAN ObjectReferenced = FALSE;
+// PPOLICY_AUDIT_RECORD AuditRecord = NULL;
+// ULONG AuditFullQueryInfoLength = sizeof (POLICY_AUDIT_FULL_QUERY_INFO);
+// BOOLEAN UpdateAttribute = FALSE;
+//
+// //
+// // Read the Audit Log Full Information from the PolAdtFL attribute of the Lsa
+// // Database object.
+// //
+//
+// Status = LsapDbReadAttributeObject(
+// LsapDbHandle,
+// &LsapDbNames[PolAdtFL],
+// PolicyAuditFullQueryInfo,
+// &AuditFullQueryInfoLength
+// );
+//
+// if (!NT_SUCCESS(Status)) {
+//
+// //
+// // The Audit Full Info was not successfully read. This may be
+// // because the attribute does not exist.
+// //
+//
+// if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+//
+// LsapLogError(
+// "LsapAdtUpdateAuditLogFullInfo: Read Audit Log Full Info returned 0x%lx\n",
+// Status
+// );
+//
+// goto QueryAuditLogFullInfoError;
+// }
+//
+// //
+// // Write default Audit Log Full Info to the PolAdtFL attribute
+// // of the Policy Object. This is temporary, to cater for
+// // existing Policy Databases.
+// //
+//
+// PolicyAuditFullQueryInfo->LogIsFull = FALSE;
+// PolicyAuditFullQueryInfo->ShutDownOnFull = FALSE;
+// UpdateAttribute = TRUE;
+//
+// } else {
+//
+// //
+// // The Audit Full Info was successfully read. If this info indicates
+// // that the Audit Log is full, optionally challenge it by actually trying
+// // to write to the log, because it may be out of date.
+// //
+// // * Passage of time may have caused more audit records to fall
+// // outwith the Retention Period (if applicable).
+// //
+// // * Action taken such as clearing the Audit Log.
+// //
+//
+// if (!((Options & LSAP_ADT_LOG_FULL_UPDATE) &&
+// PolicyAuditFullQueryInfo->LogIsFull)) {
+//
+// goto QueryAuditLogFullInfoFinish;
+// }
+//
+// //
+// // The log is apparently full. Allocate memory for a special Audit
+// // Record that we will attempt to write to the Audit Log.
+// //
+//
+// AuditRecord = LsapAllocateLsaHeap(sizeof(POLICY_AUDIT_RECORD));
+//
+// if (AuditRecord == NULL) {
+//
+// goto QueryAuditLogFullInfoError;
+// }
+//
+// AuditRecord->AuditRecordLength = sizeof(POLICY_AUDIT_RECORD);
+// AuditRecord->AuditEventType = AuditEventLogNoLongerFull;
+// AuditRecord->AuditInformationOffset = 0;
+//
+// //
+// // Try to append the record to the log file, followed by any
+// // others on the queue.
+// //
+//
+// Status = LsapAdtWriteLog(
+// AuditRecord,
+// LSAP_ADT_LOG_QUEUE_PREPEND
+// );
+//
+// if (!NT_SUCCESS(Status)) {
+//
+// //
+// // Unable to write to the Audit Log, possibly because it
+// // is full. Whatever the reason, we will not change the
+// // state of the information returned from the LSA Database.
+// // Mask out any error.
+// //
+//
+// Status = STATUS_SUCCESS;
+//
+// goto QueryAuditLogFullInfoFinish;
+// }
+//
+// PolicyAuditFullQueryInfo->LogIsFull = FALSE;
+// }
+//
+// //
+// // Write the Audit Full Information to the LSA Database.
+// // This involves a complete Database transaction.
+// //
+//
+// Status = LsapDbReferenceObject(
+// LsapDbHandle,
+// 0,
+// PolicyObject,
+// LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+// );
+//
+// if (!NT_SUCCESS(Status)) {
+//
+// goto QueryAuditLogFullInfoError;
+// }
+//
+// ObjectReferenced = TRUE;
+//
+// //
+// // Write the Audit Full information
+// //
+//
+// Status = LsapDbWriteAttributeObject(
+// LsapDbHandle,
+// &LsapDbNames[PolAdtFL],
+// &LsapAdtLogFullInformation,
+// sizeof( POLICY_AUDIT_FULL_QUERY_INFO )
+// );
+//
+// if (!NT_SUCCESS(Status)) {
+//
+// LsapLogError(
+// "LsapAdtInitialize: Write Audit Log Full Info returned 0x%lx\n",
+// Status
+// );
+//
+// goto QueryAuditLogFullInfoError;
+// }
+//
+//QueryAuditLogFullInfoFinish:
+//
+// //
+// // Finish any outstanding transaction.
+// //
+//
+// if (ObjectReferenced) {
+//
+// Status = LsapDbDereferenceObject(
+// &LsapDbHandle,
+// PolicyObject,
+// (LSAP_DB_RELEASE_LOCK |
+// LSAP_DB_FINISH_TRANSACTION),
+// (SECURITY_DB_DELTA_TYPE) 0,
+// Status
+// );
+//
+// ObjectReferenced = FALSE;
+//
+// if (!NT_SUCCESS(Status)) {
+//
+// LsapLogError(
+// "LsapAdtInitialize: Dereference Policy Object returned 0x%lx\n",
+// Status
+// );
+//
+// goto QueryAuditLogFullInfoError;
+// }
+// }
+//
+// //
+// // If we allocated an Audit Record, free it.
+// //
+//
+// if (AuditRecord != NULL) {
+//
+// LsapFreeLsaHeap( AuditRecord );
+// AuditRecord = NULL;
+// }
+//
+// return(Status);
+//
+//QueryAuditLogFullInfoError:
+//
+// goto QueryAuditLogFullInfoFinish;
+}
+
+
+NTSTATUS
+LsapAdtSignalLogFull(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function handles an Audit Log Full condition. The local policy
+ is checked to see if a system shutdown is to be initiated. The
+ Audit Log Information is updated to reflect the log full condition.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All result codes returned are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ BOOLEAN ShutDownSystem = FALSE;
+ HANDLE LsaProcessTokenHandle = NULL;
+ BOOLEAN LsaProcessTokenOpened = FALSE;
+ TOKEN_PRIVILEGES PrivilegesToBeChanged;
+ ULONG ReturnLength;
+
+ //
+ // Get the lock for LsapAdtSignalFullInProgress, which indicates
+ // that we are currently setting the log full flag.
+ //
+
+ Status = RtlEnterCriticalSection(
+ &LsapAdtLogFullLock
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // If we are already signalling that the log is full, don't try this
+ // now.
+ //
+
+ if (LsapAdtSignalFullInProgress) {
+
+ RtlLeaveCriticalSection(
+ &LsapAdtLogFullLock
+ );
+ return(STATUS_SUCCESS);
+ }
+
+ LsapAdtSignalFullInProgress = TRUE;
+
+ RtlLeaveCriticalSection(
+ &LsapAdtLogFullLock
+ );
+
+
+ //
+ // Set the Audit Log Full Policy Information to reflect the log full condition.
+ // There is an in-memory copy and a copy in the LSA Database.
+ //
+
+ LsapAdtLogFullInformation.LogIsFull = TRUE;
+
+ Status = LsarSetInformationPolicy(
+ LsapDbHandle,
+ PolicyAuditFullSetInformation,
+ (PLSAPR_POLICY_INFORMATION) &LsapAdtLogFullInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapAdtAppendLog - LsarSetInformationPolicy for Audit Log Full returned 0x%lx\n",
+ Status
+ );
+
+ goto SignalLogFullError;
+ }
+
+ //
+ // If requested, set flag so that we will shutdown the system, else
+ // take no action. If the system is not shut down, subsequent Audit
+ // Records will be lost. If the system is shut down, Audit Records
+ // generated after the reboot will be cached until system initialization
+ // is complete.
+ //
+
+ if (LsapAdtLogFullInformation.ShutDownOnFull) {
+
+ ShutDownSystem = TRUE;
+ }
+
+// //
+// // Set the Audit Log information to indicate that the Audit Log
+// // is full. This will be detected on system reload by winlogon
+// // by calling the LsaQueryInformationPolicy API. winlogon will
+// // then permit logon only to the ADMIN account to allow the user to
+// // correct the Audit Log Full condition.
+// //
+//
+// Status = LsarSetInformationPolicy(
+// LsapDbHandle,
+// PolicyAuditLogInformation,
+// (PLSAPR_POLICY_INFORMATION) &LsapAdtLogInformation
+// );
+//
+// if (!NT_SUCCESS(Status)) {
+//
+// LsapLogError(
+// "LsapAdtAppendLog - LsarSetInformationPolicy for Audit Log returned 0x%lx\n",
+// Status
+// );
+//
+// goto SignalLogFullError;
+// }
+
+ //
+ // Shutdown the system if necessary.
+ //
+
+ if (ShutDownSystem) {
+
+ //
+ // Since we, the LSA, are a local client of SCREG.EXE, we need
+ // SE_SHUTDOWN_PRIVILEGE to be enabled so that we can shutdown
+ // the system.
+ //
+
+ PrivilegesToBeChanged.PrivilegeCount = 1;
+ PrivilegesToBeChanged.Privileges[0].Luid =
+ RtlConvertUlongToLuid(SE_SHUTDOWN_PRIVILEGE);
+
+ PrivilegesToBeChanged.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ //
+ // Open the LSA Process Token and turn on the privilege.
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES,
+ &LsaProcessTokenHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SignalLogFullError;
+ }
+
+ Status = NtAdjustPrivilegesToken(
+ LsaProcessTokenHandle,
+ FALSE,
+ &PrivilegesToBeChanged,
+ 0,
+ NULL,
+ &ReturnLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SignalLogFullError;
+ }
+
+ Status = NtClose( LsaProcessTokenHandle );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SignalLogFullError;
+ }
+
+ LsaProcessTokenHandle = NULL;
+
+ //
+ // Now initiate a system shutdown request.
+ //
+
+ if (!InitiateSystemShutdownW(
+ L"",
+ L"System shutdown initiated",
+ (DWORD) LSAP_ADT_LOG_FULL_SHUTDOWN_TIMEOUT,
+ TRUE,
+ TRUE
+ )) {
+
+ //
+ // BUGBUG - ScottBi - Don't know how to get the last Nt error
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto SignalLogFullError;
+ }
+
+ ShutDownSystem = FALSE;
+ }
+
+SignalLogFullFinish:
+
+
+ //
+ // Indicate that we are done setting the log full flag
+ //
+
+ SecondaryStatus = RtlEnterCriticalSection(
+ &LsapAdtLogFullLock
+ );
+
+ //
+ // We can't do much about this, so assert success
+ //
+
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+
+ LsapAdtSignalFullInProgress = FALSE;
+
+ RtlLeaveCriticalSection(
+ &LsapAdtLogFullLock
+ );
+
+
+ //
+ // If necessary, close the Lsa Process Token handle.
+ //
+
+ if (LsaProcessTokenHandle != NULL) {
+
+ SecondaryStatus = NtClose( LsaProcessTokenHandle );
+
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+ }
+
+ return(Status);
+
+SignalLogFullError:
+
+ goto SignalLogFullFinish;
+}
+
+
+
+NTSTATUS
+LsapAdtLogQueuedEvents(
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function clears the Audit Log queue. Each Audit Record on the queue
+ is either written to the Audit Log and removed from the queue, or
+ discarded depending on the option specified.
+
+Arguments:
+
+ Options - Specify optional actions to be taken.
+
+ LSAP_ADT_LOG_QUEUE_DISCARD - Discard the queue without writing
+ its contents to the Audit Log. If this flag is not specified,
+ the queue will be written to the log prior to discard.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_LOG_FILE_FULL - The Audit Log became full.
+--*/
+
+{
+ return( LsapAdtWriteLog( NULL, Options ));
+}
+
+
+
+NTSTATUS
+LsapAdtAcquireLogQueueLock(
+ )
+
+/*++
+
+Routine Description:
+
+ This function acquires the LSA Audit Log Queue Lock. This lock serializes
+ all updates to the Audit Log Queue.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = RtlEnterCriticalSection(&LsapAdtQueueLock);
+
+ return Status;
+}
+
+
+VOID
+LsapAdtReleaseLogQueueLock(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function releases the LSA Audit Log Queue Lock. This lock serializes
+ updates to the Audit Log Queue.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None. Any error occurring within this routine is an internal error.
+
+--*/
+
+{
+ RtlLeaveCriticalSection(&LsapAdtQueueLock);
+}
+
+
+
+ /////////////////////////////////////////////////////////
+ // //
+ // //
+ // New auditing routines //
+ // //
+ // This code will eventually replace //
+ // most of the above. //
+ // //
+ // //
+ /////////////////////////////////////////////////////////
+
+
+
+
+NTSTATUS
+LsapAdtDemarshallAuditInfo(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will walk down a marshalled audit parameter
+ array and unpack it so that its information may be passed
+ into the event logging service.
+
+ Three parallel data structures are maintained:
+
+ StringArray - Array of Unicode string structures. This array
+ is used primarily as temporary storage for returned string
+ structures.
+
+ StringPointerArray - Array of pointers to Unicode string structures.
+
+ FreeWhenDone - Array of booleans describing how to dispose of each
+ of the strings pointed to by the StringPointerArray.
+
+
+ Note that entries in the StringPointerArray are contiguous, but that
+ there may be gaps in the StringArray structure. For each entry in the
+ StringPointerArray there will be a corresponding entry in the FreeWhenDone
+ array. If the entry for a particular string is TRUE, the storage for
+ the string buffer will be released to the process heap.
+
+
+
+ StringArray
+ Other strings
+ +----------------+
+ | |<-----------+ +----------------+
+ | | | | |<-------------------+
+ +----------------+ | | | |
+ | UNUSED | | +----------------+ |
+ | | | |
+ +----------------+ | |
+ | |<------+ | +----------------+ |
+ | | | | | |<-----------+ |
+ +----------------+ | | | | | |
+ | UNUSED | | | +----------------+ | |
+ | | | | | |
+ +----------------+ | | | |
+ | |<--+ | | | |
+ | | | | | | |
+ +----------------+ | | | | |
+ | | | | | | |
+ | | | | | StringPointerArray | |
+ .... | | | | |
+ | | | +----------------+ | |
+ | | +-----| | | |
+ | | +----------------+ | |
+ | | | |---------+ |
+ | | +----------------+ |
+ | +----------| | |
+ | +----------------+ |
+ | | |-----------------+
+ | +----------------+
+ +--------------| |
+ +----------------+
+ | |
+ +----------------+
+ | |
+ +----------------+
+ | |
+ ....
+
+
+Arguments:
+
+ AuditParameters - Receives a pointer to an audit
+ parameters array in self-relative form.
+
+Return Value:
+
+
+--*/
+
+{
+
+ ULONG ParameterCount;
+ USHORT i;
+ PUNICODE_STRING StringPointerArray[30];
+ BOOLEAN FreeWhenDone[30];
+ UNICODE_STRING StringArray[30];
+ USHORT StringIndex = 0;
+ UNICODE_STRING DashString;
+ BOOLEAN FreeDash;
+ NTSTATUS Status;
+ PUNICODE_STRING SourceModule;
+ PSID UserSid;
+
+
+ Status= LsapAdtBuildDashString(
+ &DashString,
+ &FreeDash
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ ParameterCount = AuditParameters->ParameterCount;
+
+ //
+ // Parameter 0 will always be the user SID. Convert the
+ // offset to the SID into a pointer.
+ //
+
+ ASSERT( AuditParameters->Parameters[0].Type == SeAdtParmTypeSid );
+
+
+
+ UserSid = (PSID)AuditParameters->Parameters[0].Address;
+
+
+
+ //
+ // Parameter 1 will always be the Source Module (or Subsystem Name).
+ // Unpack this now.
+ //
+
+ ASSERT( AuditParameters->Parameters[1].Type == SeAdtParmTypeString );
+
+
+
+ SourceModule = (PUNICODE_STRING)AuditParameters->Parameters[1].Address;
+
+
+ for (i=2; i<ParameterCount; i++) {
+
+ switch ( AuditParameters->Parameters[i].Type ) {
+ case SeAdtParmTypeNone:
+ {
+ StringPointerArray[StringIndex] = &DashString;
+
+ FreeWhenDone[StringIndex] = FALSE;
+
+ StringIndex++;
+
+ break;
+ }
+ case SeAdtParmTypeString:
+ {
+ StringPointerArray[StringIndex] =
+ (PUNICODE_STRING)AuditParameters->Parameters[i].Address;
+
+ FreeWhenDone[StringIndex] = FALSE;
+
+ StringIndex++;
+
+ break;
+ }
+ case SeAdtParmTypeFileSpec:
+ {
+ //
+ // Same as a string, except we must attempt to replace
+ // device information with a drive letter.
+ //
+
+ StringPointerArray[StringIndex] =
+ (PUNICODE_STRING)AuditParameters->Parameters[i].Address;
+
+
+ //
+ // This may not do anything, in which case just audit what
+ // we have.
+ //
+
+ LsapAdtSubstituteDriveLetter( StringPointerArray[StringIndex] );
+
+ FreeWhenDone[StringIndex] = FALSE;
+
+ StringIndex++;
+
+ break;
+ }
+ case SeAdtParmTypeUlong:
+ {
+ ULONG Data;
+
+ Data = AuditParameters->Parameters[i].Data[0];
+
+ Status = LsapAdtBuildUlongString(
+ Data,
+ &StringArray[StringIndex],
+ &FreeWhenDone[StringIndex]
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ StringPointerArray[StringIndex] = &StringArray[StringIndex];
+
+
+ } else {
+
+ //
+ // Couldn't allocate memory for that string,
+ // use the Dash string that we've already created.
+ //
+
+ StringPointerArray[StringIndex] = &DashString;
+ FreeWhenDone[StringIndex] = FALSE;
+ }
+
+ StringIndex++;
+
+ break;
+ }
+ case SeAdtParmTypeSid:
+ {
+ PSID Sid;
+
+ Sid = (PSID)AuditParameters->Parameters[i].Address;
+
+ Status = LsapAdtBuildSidString(
+ Sid,
+ &StringArray[StringIndex],
+ &FreeWhenDone[StringIndex]
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ StringPointerArray[StringIndex] = &StringArray[StringIndex];
+
+ } else {
+
+ //
+ // Couldn't allocate memory for that string,
+ // use the Dash string that we've already created.
+ //
+
+ StringPointerArray[StringIndex] = &DashString;
+ FreeWhenDone[StringIndex] = FALSE;
+ }
+
+ StringIndex++;
+
+
+ break;
+ }
+ case SeAdtParmTypeLogonId:
+ {
+ PLUID LogonId;
+ ULONG j;
+
+ LogonId = (PLUID)(&AuditParameters->Parameters[i].Data[0]);
+
+ Status = LsapAdtBuildLogonIdStrings(
+ LogonId,
+ &StringArray [ StringIndex ],
+ &FreeWhenDone[ StringIndex ],
+ &StringArray [ StringIndex + 1 ],
+ &FreeWhenDone[ StringIndex + 1 ],
+ &StringArray [ StringIndex + 2 ],
+ &FreeWhenDone[ StringIndex + 2 ]
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ for (j=0; j<3; j++) {
+
+ StringPointerArray[StringIndex] = &StringArray[StringIndex];
+ StringIndex++;
+ }
+
+ //
+ // Finished, break out to surrounding loop.
+ //
+
+ break;
+
+ } else {
+
+ //
+ // Do nothing, fall through to the NoLogonId case
+ //
+
+ }
+ }
+ case SeAdtParmTypeNoLogonId:
+ {
+ ULONG j;
+ //
+ // Create three "-" strings.
+ //
+
+ for (j=0; j<3; j++) {
+
+ StringPointerArray[ StringIndex ] = &DashString;
+ FreeWhenDone[ StringIndex ] = FALSE;
+ StringIndex++;
+ }
+
+ break;
+ }
+ case SeAdtParmTypeAccessMask:
+ {
+ PUNICODE_STRING ObjectTypeName;
+ ULONG ObjectTypeNameIndex;
+ ACCESS_MASK Accesses;
+
+ ObjectTypeNameIndex = AuditParameters->Parameters[i].Data[1];
+ ObjectTypeName = AuditParameters->Parameters[ObjectTypeNameIndex].Address;
+ Accesses= AuditParameters->Parameters[i].Data[0];
+
+ //
+ // We can determine the index to the ObjectTypeName
+ // parameter since it was stored away in the Data[1]
+ // field of this parameter.
+ //
+
+ Status = LsapAdtBuildAccessesString(
+ SourceModule,
+ ObjectTypeName,
+ Accesses,
+ &StringArray [ StringIndex ],
+ &FreeWhenDone[ StringIndex ]
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ StringPointerArray[ StringIndex ] = &StringArray[ StringIndex ];
+
+ } else {
+
+ //
+ // That didn't work, use the Dash string instead.
+ //
+
+ StringPointerArray[ StringIndex ] = &DashString;
+ FreeWhenDone [ StringIndex ] = FALSE;
+ }
+
+ StringIndex++;
+
+ break;
+ }
+ case SeAdtParmTypePrivs:
+ {
+
+ PPRIVILEGE_SET Privileges = (PPRIVILEGE_SET)AuditParameters->Parameters[i].Address;
+
+ Status = LsapBuildPrivilegeAuditString(
+ Privileges,
+ &StringArray [ StringIndex ],
+ &FreeWhenDone[ StringIndex ]
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ StringPointerArray[ StringIndex ] = &StringArray[ StringIndex ];
+
+ } else {
+
+ //
+ // That didn't work, use the Dash string instead.
+ //
+
+ StringPointerArray[ StringIndex ] = &DashString;
+ FreeWhenDone [ StringIndex ] = FALSE;
+ }
+
+ StringIndex++;
+
+ break;
+ }
+ }
+ }
+
+ //
+ // Probably have to do this from somewhere else eventually, but for now
+ // do it from here.
+ //
+
+
+
+ Status = ElfReportEventW (
+ LsapAdtLogHandle,
+ AuditParameters->Type,
+ (USHORT)AuditParameters->CategoryId,
+ AuditParameters->AuditId,
+ UserSid,
+ StringIndex,
+ 0,
+ StringPointerArray,
+ NULL,
+ 0,
+ NULL,
+ NULL
+ );
+
+ //
+ // cleanup
+ //
+
+ for (i=0; i<StringIndex; i++) {
+
+ if (FreeWhenDone[i]) {
+ LsapFreeLsaHeap( StringPointerArray[i]->Buffer );
+ }
+ }
+
+ //
+ // If we are in the middle of shutdown, we can tolerate this failure.
+ //
+
+ if ( (Status == RPC_NT_UNKNOWN_IF) && LsapShutdownInProgress ) {
+ Status = STATUS_SUCCESS;
+ }
+ return( Status );
+}
+
+
+
+
+VOID
+LsapAdtNormalizeAuditInfo(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will walk down a marshalled audit parameter
+ array and turn it into an Absolute format data structure.
+
+
+Arguments:
+
+ AuditParameters - Receives a pointer to an audit
+ parameters array in self-relative form.
+
+Return Value:
+
+ TRUE on success, FALSE on failure.
+
+--*/
+
+{
+
+ ULONG ParameterCount;
+ ULONG i;
+ ULONG Address;
+ PUNICODE_STRING Unicode;
+
+
+ if ( !(AuditParameters->Flags & SE_ADT_PARAMETERS_SELF_RELATIVE)) {
+
+ return;
+ }
+
+ ParameterCount = AuditParameters->ParameterCount;
+
+ for (i=0; i<ParameterCount; i++) {
+
+ switch ( AuditParameters->Parameters[i].Type ) {
+ case SeAdtParmTypeNone:
+ case SeAdtParmTypeUlong:
+ case SeAdtParmTypeLogonId:
+ case SeAdtParmTypeNoLogonId:
+ case SeAdtParmTypeAccessMask:
+ {
+
+ break;
+ }
+ case SeAdtParmTypeString:
+ case SeAdtParmTypeFileSpec:
+ {
+ Address = (ULONG)AuditParameters->Parameters[i].Address;
+ Address += (ULONG)AuditParameters;
+
+ AuditParameters->Parameters[i].Address = (PVOID)Address;
+
+ Unicode = (PUNICODE_STRING)Address;
+ Unicode->Buffer = (PWSTR)((PCHAR)Unicode->Buffer + (ULONG)AuditParameters);
+
+ break;
+ }
+ case SeAdtParmTypeSid:
+ {
+ PSID Sid;
+
+ Sid = (PSID) AuditParameters->Parameters[i].Address;
+
+ Sid = (PSID) ((PCHAR)Sid + (ULONG)AuditParameters);
+
+ AuditParameters->Parameters[i].Address = (PVOID)Sid;
+
+ break;
+ }
+ case SeAdtParmTypePrivs:
+ {
+ PPRIVILEGE_SET Privileges;
+
+ Privileges = (PPRIVILEGE_SET) AuditParameters->Parameters[i].Address;
+
+ Privileges = (PPRIVILEGE_SET) ((PCHAR)Privileges + (ULONG)AuditParameters);
+
+ AuditParameters->Parameters[i].Address = (PVOID)Privileges;
+
+ break;
+ }
+ }
+ }
+}
+
+
+
+
+NTSTATUS
+LsapAdtMarshallAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
+ OUT PSE_ADT_PARAMETER_ARRAY *MarshalledAuditParameters
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will take an AuditParamters structure and create
+ a new AuditParameters structure that is suitable for placing
+ to LSA. It will be in self-relative form and allocated as
+ a single chunk of memory.
+
+Arguments:
+
+
+ AuditParameters - A filled in set of AuditParameters to be marshalled.
+
+ MarshalledAuditParameters - Returns a pointer to a block of heap memory
+ containing the passed AuditParameters in self-relative form suitable
+ for passing to LSA.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+ ULONG TotalSize = sizeof( SE_ADT_PARAMETER_ARRAY );
+ PUNICODE_STRING TargetString;
+ PCHAR Base;
+ ULONG BaseIncr;
+ ULONG Size;
+
+
+
+ //
+ // Calculate the total size required for the passed AuditParameters
+ // block. This calculation will probably be an overestimate of the
+ // amount of space needed, because data smaller that 2 dwords will
+ // be stored directly in the parameters structure, but their length
+ // will be counted here anyway. The overestimate can't be more than
+ // 24 dwords, and will never even approach that amount, so it isn't
+ // worth the time it would take to avoid it.
+ //
+
+ for (i=0; i<AuditParameters->ParameterCount; i++) {
+ Size = AuditParameters->Parameters[i].Length;
+ TotalSize = TotalSize + (ULONG)LongAlign( Size );
+ }
+
+ //
+ // Allocate a big enough block of memory to hold everything.
+ // If it fails, quietly abort, since there isn't much else we
+ // can do.
+ //
+
+ *MarshalledAuditParameters = LsapAllocateLsaHeap( TotalSize );
+
+ if (*MarshalledAuditParameters == NULL) {
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ RtlMoveMemory (
+ *MarshalledAuditParameters,
+ AuditParameters,
+ sizeof( SE_ADT_PARAMETER_ARRAY )
+ );
+
+ (*MarshalledAuditParameters)->Length = TotalSize;
+ (*MarshalledAuditParameters)->Flags = SE_ADT_PARAMETERS_SELF_RELATIVE;
+
+ //
+ // Start walking down the list of parameters and marshall them
+ // into the target buffer.
+ //
+
+ Base = (PCHAR) ((PCHAR)(*MarshalledAuditParameters) + sizeof( SE_ADT_PARAMETER_ARRAY ));
+
+ for (i=0; i<AuditParameters->ParameterCount; i++) {
+
+
+ switch (AuditParameters->Parameters[i].Type) {
+ case SeAdtParmTypeNone:
+ case SeAdtParmTypeUlong:
+ case SeAdtParmTypeLogonId:
+ case SeAdtParmTypeNoLogonId:
+ case SeAdtParmTypeAccessMask:
+ {
+ //
+ // Nothing to do for this
+ //
+
+ break;
+
+ }
+ case SeAdtParmTypeString:
+ {
+ PUNICODE_STRING SourceString;
+
+ //
+ // We must copy the body of the unicode string
+ // and then copy the body of the string. Pointers
+ // must be turned into offsets.
+
+ TargetString = (PUNICODE_STRING)Base;
+
+ SourceString = AuditParameters->Parameters[i].Address;
+
+ *TargetString = *SourceString;
+
+ //
+ // Reset the data pointer in the output parameters to
+ // 'point' to the new string structure.
+ //
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ Base += sizeof( UNICODE_STRING );
+
+ RtlCopyMemory( Base, SourceString->Buffer, SourceString->Length );
+
+ //
+ // Make the string buffer in the target string point to where we
+ // just copied the data.
+ //
+
+ TargetString->Buffer = (PWSTR)(Base - (ULONG)(*MarshalledAuditParameters));
+
+ BaseIncr = (ULONG)LongAlign(SourceString->Length);
+
+ Base += BaseIncr;
+
+ ASSERT( (ULONG)Base <= (ULONG)(*MarshalledAuditParameters) + TotalSize );
+
+ break;
+ }
+ case SeAdtParmTypeSid:
+ {
+ PSID TargetSid = (PSID) Base;
+ PSID SourceSid = AuditParameters->Parameters[i].Address;
+
+ //
+ // Copy the Sid into the output buffer
+ //
+
+ RtlCopyMemory( TargetSid, SourceSid, RtlLengthSid( SourceSid ) );
+
+ //
+ // Reset the 'address' of the Sid to be its offset in the
+ // buffer.
+ //
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ BaseIncr = (ULONG)LongAlign(RtlLengthSid( SourceSid ));
+
+ Base += BaseIncr;
+
+ ASSERT( (ULONG)Base <= (ULONG)(*MarshalledAuditParameters) + TotalSize );
+
+ break;
+ }
+ case SeAdtParmTypePrivs:
+ {
+ PPRIVILEGE_SET TargetPrivileges = (PPRIVILEGE_SET) Base;
+ PPRIVILEGE_SET SourcePrivileges = AuditParameters->Parameters[i].Address;
+
+ RtlCopyMemory( TargetPrivileges, SourcePrivileges, LsapPrivilegeSetSize( SourcePrivileges ));
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ BaseIncr = (ULONG)LongAlign(LsapPrivilegeSetSize( SourcePrivileges ));
+
+ Base += BaseIncr;
+
+ break;
+ }
+ default:
+ {
+ //
+ // We got passed junk, complain.
+ //
+
+ ASSERT( FALSE );
+ break;
+ }
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+
+NTSTATUS
+LsaIAuditSamEvent(
+ IN NTSTATUS PassedStatus,
+ IN ULONG AuditId,
+ IN PSID DomainSid,
+ IN PULONG MemberRid OPTIONAL,
+ IN PSID MemberSid OPTIONAL,
+ IN PUNICODE_STRING AccountName OPTIONAL,
+ IN PUNICODE_STRING DomainName,
+ IN PULONG AccountRid OPTIONAL,
+ IN PPRIVILEGE_SET Privileges OPTIONAL
+ )
+/*++
+
+Abstract:
+
+ This routine produces an audit record representing an account
+ operation.
+
+ This routine goes through the list of parameters and adds a string
+ representation of each (in order) to an audit message. Note that
+ the full complement of account audit message formats is achieved by
+ selecting which optional parameters to include in this call.
+
+ In addition to any parameters passed below, this routine will ALWAYS
+ add the impersonation client's user name, domain, and logon ID as
+ the LAST parameters in the audit message.
+
+
+Parmeters:
+
+ AuditId - Specifies the message ID of the audit being generated.
+
+ DomainSid - This parameter results in a SID string being generated
+ ONLY if neither the MemberRid nor AccountRid parameters are
+ passed. If either of those parameters are passed, this parameter
+ is used as a prefix of a SID.
+
+ MemberRid - This optional parameter, if present, is added to the end of
+ the DomainSid parameter to produce a "Member" sid. The resultant
+ member SID is then used to build a sid-string which is added to the
+ audit message following all preceeding parameters.
+ This parameter supports global group membership change audits, where
+ member IDs are always relative to a local domain.
+
+ MemberSid - This optional parameter, if present, is converted to a
+ SID string and added following preceeding parameters. This parameter
+ is generally used for describing local group (alias) members, where
+ the member IDs are not relative to a local domain.
+
+ AccountName - This optional parameter, if present, is added to the audit
+ message without change following any preceeding parameters.
+ This parameter is needed for almost all account audits and does not
+ need localization.
+
+ DomainName - This optional parameter, if present, is added to the audit
+ message without change following any preceeding parameters.
+ This parameter is needed for almost all account audits and does not
+ need localization.
+
+
+ AccountRid - This optional parameter, if present, is added to the end of
+ the DomainSid parameter to produce an "Account" sid. The resultant
+ Account SID is then used to build a sid-string which is added to the
+ audit message following all preceeding parameters.
+ This parameter supports audits that include "New account ID" or
+ "Target Account ID" fields.
+
+ Privileges - The privileges passed via this optional parameter,
+ if present, will be converted to string format and added to the
+ audit message following any preceeding parameters. NOTE: the
+ caller is responsible for freeing the privilege_set (in fact,
+ it may be on the stack). ALSO NOTE: The privilege set will be
+ destroyed by this call (due to use of the routine used to
+ convert the privilege values to privilege names).
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LUID LogonId;
+ PSID NewAccountSid = NULL;
+ PSID NewMemberSid = NULL;
+ PSID SidPointer;
+ PSID ClientSid = NULL;
+ PTOKEN_USER TokenUserInformation;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ UCHAR AccountSidBuffer[256];
+ UCHAR MemberSidBuffer[256];
+ UCHAR SubAuthorityCount;
+ UNICODE_STRING SubsystemName;
+ ULONG LengthRequired;
+
+ Status = LsapQueryClientInfo(
+ &TokenUserInformation,
+ &LogonId
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ ClientSid = TokenUserInformation->User.Sid;
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = SE_CATEGID_ACCOUNT_MANAGEMENT;
+ AuditParameters.AuditId = AuditId;
+ AuditParameters.Type = (NT_SUCCESS(PassedStatus) ? EVENTLOG_AUDIT_SUCCESS : EVENTLOG_AUDIT_FAILURE );
+ AuditParameters.ParameterCount = 0;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, ClientSid );
+
+ AuditParameters.ParameterCount++;
+
+ RtlInitUnicodeString( &SubsystemName, L"Security" );
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+ if (ARGUMENT_PRESENT(MemberRid)) {
+
+ //
+ // Add a member SID string to the audit message
+ //
+ // Domain Sid + Member Rid = Final SID.
+
+ SubAuthorityCount = *RtlSubAuthorityCountSid( DomainSid );
+
+ if ( (LengthRequired = RtlLengthRequiredSid( SubAuthorityCount + 1 )) > 256 ) {
+
+ NewMemberSid = LsapAllocateLsaHeap( LengthRequired );
+
+ SidPointer = NewMemberSid;
+
+ } else {
+
+ SidPointer = (PSID)MemberSidBuffer;
+ }
+
+ Status = RtlCopySid (
+ LengthRequired,
+ SidPointer,
+ DomainSid
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ *(RtlSubAuthoritySid( SidPointer, SubAuthorityCount )) = *MemberRid;
+ *RtlSubAuthorityCountSid( SidPointer ) = SubAuthorityCount + 1;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, SidPointer );
+
+ AuditParameters.ParameterCount++;
+ }
+
+ if (ARGUMENT_PRESENT(MemberSid)) {
+
+ //
+ // Add a member SID string to the audit message
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, MemberSid );
+
+ AuditParameters.ParameterCount++;
+ }
+
+ if (ARGUMENT_PRESENT(AccountName)) {
+
+ //
+ // Add a UNICODE_STRING to the audit message
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, AccountName );
+
+ AuditParameters.ParameterCount++;
+ }
+
+
+ if (ARGUMENT_PRESENT(DomainName)) {
+
+ //
+ // Add a UNICODE_STRING to the audit message
+ //
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, DomainName );
+
+ AuditParameters.ParameterCount++;
+ }
+
+
+
+
+ if (ARGUMENT_PRESENT(DomainSid) &&
+ !(ARGUMENT_PRESENT(MemberRid) || ARGUMENT_PRESENT(AccountRid))
+ ) {
+
+ //
+ // Add the domain SID as a SID string to the audit message
+ //
+ // Just the domain SID.
+ //
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, DomainSid );
+
+ AuditParameters.ParameterCount++;
+
+ }
+
+ if (ARGUMENT_PRESENT(AccountRid)) {
+
+ //
+ // Add a member SID string to the audit message
+ // Domain Sid + account Rid = final sid
+ //
+
+ SubAuthorityCount = *RtlSubAuthorityCountSid( DomainSid );
+
+ if ( (LengthRequired = RtlLengthRequiredSid( SubAuthorityCount + 1 )) > 256 ) {
+
+ NewAccountSid = LsapAllocateLsaHeap( LengthRequired );
+
+ SidPointer = NewMemberSid;
+
+ } else {
+
+ SidPointer = (PSID)AccountSidBuffer;
+ }
+
+
+ Status = RtlCopySid (
+ LengthRequired,
+ SidPointer,
+ DomainSid
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ *(RtlSubAuthoritySid( SidPointer, SubAuthorityCount )) = *AccountRid;
+ *RtlSubAuthorityCountSid( SidPointer ) = SubAuthorityCount + 1;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, SidPointer );
+
+ AuditParameters.ParameterCount++;
+ }
+
+ //
+ // Now add the caller information
+ //
+ // Caller name
+ // Caller domain
+ // Caller logon ID
+ //
+
+
+ LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, LogonId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Add any privileges
+ //
+
+ if (ARGUMENT_PRESENT(Privileges)) {
+
+ LsapSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, Privileges );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Now write out the audit record to the audit log
+ //
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ //
+ // And clean up any allocated memory
+ //
+
+ if ( NewMemberSid != NULL ) {
+ LsapFreeLsaHeap( NewMemberSid );
+ }
+
+ if ( NewAccountSid != NULL ) {
+ LsapFreeLsaHeap( NewAccountSid );
+ }
+
+ if ( TokenUserInformation != NULL ) {
+ LsapFreeLsaHeap( TokenUserInformation );
+ }
+}
+
+
+VOID
+LsapAdtAuditLogonProcessRegistration(
+ IN PLSAP_AU_REGISTER_CONNECT_INFO ConnectInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Audits the registration of a logon process
+
+Arguments:
+
+ ConnectInfo - Supplies the connection information for the new
+ logon process.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ NTSTATUS Status;
+ ANSI_STRING AnsiString;
+ UNICODE_STRING Unicode;
+ PSZ LogonProcessNameBuffer;
+ PSID LocalSystemSid;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ if ( !LsapAdtEventsInformation.AuditingMode ) {
+ return;
+ }
+
+ if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
+ return;
+ }
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &LocalSystemSid
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // Must be out of memory, not much we can do here
+ //
+
+ return;
+ }
+
+ //
+ // Turn the name text in the ConnectInfo structure into
+ // something we can work with.
+ //
+
+ LogonProcessNameBuffer = (PSZ)LsapAllocateLsaHeap( ConnectInfo->LogonProcessNameLength+1 );
+
+ RtlCopyMemory(
+ LogonProcessNameBuffer,
+ ConnectInfo->LogonProcessName,
+ ConnectInfo->LogonProcessNameLength
+ );
+
+ LogonProcessNameBuffer[ConnectInfo->LogonProcessNameLength] = 0;
+ RtlInitAnsiString( &AnsiString, LogonProcessNameBuffer );
+
+ Status = RtlAnsiStringToUnicodeString( &Unicode, &AnsiString, TRUE );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // Must be out of memory, not much we can do here
+ //
+
+ RtlFreeSid( LocalSystemSid );
+ LsapFreeLsaHeap( LogonProcessNameBuffer );
+ return;
+ }
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
+ AuditParameters.AuditId = SE_AUDITID_SYSTEM_LOGON_PROC_REGISTER;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+ AuditParameters.ParameterCount = 0;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &Unicode );
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ RtlFreeSid( LocalSystemSid );
+ LsapFreeLsaHeap( LogonProcessNameBuffer );
+ RtlFreeUnicodeString( &Unicode );
+
+ return;
+}
+
+
+VOID
+LsapAdtAuditPackageLoad(
+ PUNICODE_STRING PackageFileName
+ )
+
+/*++
+
+Routine Description:
+
+ Audits the loading of an authentication package.
+
+Arguments:
+
+ PackageFileName - The name of the package being loaded.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ NTSTATUS Status;
+ PSID LocalSystemSid;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ if ( !LsapAdtEventsInformation.AuditingMode ) {
+ return;
+ }
+
+ if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
+ return;
+ }
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &LocalSystemSid
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // Must be out of memory, not much we can do here
+ //
+
+ return;
+ }
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
+ AuditParameters.AuditId = SE_AUDITID_AUTH_PACKAGE_LOAD;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+ AuditParameters.ParameterCount = 0;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, PackageFileName );
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ RtlFreeSid( LocalSystemSid );
+
+ return;
+
+}
+
+
+
+VOID
+LsaIAuditNotifyPackageLoad(
+ PUNICODE_STRING PackageFileName
+ )
+
+/*++
+
+Routine Description:
+
+ Audits the loading of an notification package.
+
+Arguments:
+
+ PackageFileName - The name of the package being loaded.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ NTSTATUS Status;
+ PSID LocalSystemSid;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ if ( !LsapAdtEventsInformation.AuditingMode ) {
+ return;
+ }
+
+ if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS)) {
+ return;
+ }
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &LocalSystemSid
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // Must be out of memory, not much we can do here
+ //
+
+ return;
+ }
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
+ AuditParameters.AuditId = SE_AUDITID_NOTIFY_PACKAGE_LOAD;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+ AuditParameters.ParameterCount = 0;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, PackageFileName );
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ RtlFreeSid( LocalSystemSid );
+
+ return;
+
+}
+
+
+VOID
+LsapAdtAuditDiscardedAudits(
+ ULONG NumberOfEventsDiscarded
+ )
+/*++
+
+Routine Description:
+
+ Audits the fact that we discarded some audits.
+
+Arguments:
+
+ NumberOfEventsDiscarded - The number of events discarded.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+ NTSTATUS Status;
+ PSID LocalSystemSid;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ if ( !LsapAdtEventsInformation.AuditingMode ) {
+ return;
+ }
+
+ if (!LsapAdtEventsInformation.EventAuditingOptions[AuditCategorySystem] & POLICY_AUDIT_EVENT_SUCCESS) {
+ return;
+ }
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &LocalSystemSid
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // Must be out of memory, not much we can do here
+ //
+
+ return;
+ }
+
+ RtlZeroMemory ((PVOID) &AuditParameters, sizeof( AuditParameters ));
+
+ AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
+ AuditParameters.AuditId = SE_AUDITID_AUDITS_DISCARDED;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+ AuditParameters.ParameterCount = 0;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, LocalSystemSid );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, NumberOfEventsDiscarded );
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ RtlFreeSid( LocalSystemSid );
+
+ return;
+}
+
+
+PLUID LsaFilterPrivileges[] =
+ {
+ &ChangeNotifyPrivilege,
+ &AuditPrivilege,
+ &CreateTokenPrivilege,
+ &AssignPrimaryTokenPrivilege,
+ &BackupPrivilege,
+ &RestorePrivilege,
+ &DebugPrivilege,
+ NULL
+ };
+
+
+VOID
+LsapAdtAuditSpecialPrivileges(
+ PPRIVILEGE_SET Privileges,
+ LUID LogonId,
+ PSID UserSid
+ )
+/*++
+
+Routine Description:
+
+ Audits the assignment of special privileges at logon time.
+
+Arguments:
+
+ Privileges - List of privileges being assigned.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PPRIVILEGE_SET Buffer;
+ PLUID *FilterPrivilege = NULL;
+ ULONG i;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+
+ if ( !LsapAdtEventsInformation.AuditingMode ) {
+ return;
+ }
+
+ if (!(LsapAdtEventsInformation.EventAuditingOptions[AuditCategoryPrivilegeUse] & POLICY_AUDIT_EVENT_SUCCESS)) {
+ return;
+ }
+
+ if ( (Privileges == NULL) || (Privileges->PrivilegeCount == 0) ) {
+ return;
+ }
+
+ //
+ // We can't need any more space than what's being passed in.
+ //
+
+ Buffer = (PPRIVILEGE_SET)LsapAllocateLsaHeap( LsapPrivilegeSetSize( Privileges ) );
+
+ if ( Buffer == NULL ) {
+ return;
+ }
+
+ Buffer->PrivilegeCount = 0;
+
+ //
+ // For each privilege in the privilege set, see if it's in the filter
+ // list.
+ //
+
+ for ( i=0; i<Privileges->PrivilegeCount; i++) {
+
+ FilterPrivilege = LsaFilterPrivileges;
+
+ do {
+
+ if ( RtlEqualLuid( &Privileges->Privilege[i].Luid, *FilterPrivilege )) {
+
+ Buffer->Privilege[Buffer->PrivilegeCount].Luid = **FilterPrivilege;
+ Buffer->PrivilegeCount++;
+ }
+
+ } while ( *++FilterPrivilege != NULL );
+ }
+
+ if ( Buffer->PrivilegeCount == 0 ) {
+ LsapFreeLsaHeap( Buffer );
+ return;
+ }
+
+ //
+ // We matched on at least one, generate an audit.
+ //
+
+ RtlZeroMemory ((PVOID) &AuditParameters, sizeof( AuditParameters ));
+
+ AuditParameters.CategoryId = SE_CATEGID_PRIVILEGE_USE;
+ AuditParameters.AuditId = SE_AUDITID_ASSIGN_SPECIAL_PRIV;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+ AuditParameters.ParameterCount = 0;
+
+ LsapSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &LsapSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, LogonId );
+ AuditParameters.ParameterCount++;
+
+ LsapSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, Buffer );
+ AuditParameters.ParameterCount++;
+
+ ( VOID ) LsapAdtWriteLog( &AuditParameters, 0 );
+
+ LsapFreeLsaHeap( Buffer );
+
+ return;
+}
+
+
+
+
+
+
+VOID
+LsapAdtSubstituteDriveLetter(
+ IN OUT PUNICODE_STRING FileName
+ )
+
+/*++
+
+Routine Description:
+
+ Takes a filename and replaces the device name part with a
+ drive letter, if possible.
+
+ The string will be edited directly in place, which means that
+ the Length field will be adjusted, and the Buffer contents will
+ be moved so that the drive letter is at the beginning of the
+ buffer. No memory will be allocated or freed.
+
+Arguments:
+
+ FileName - Supplies a pointer to a unicode string containing
+ a filename.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ WCHAR DriveLetter;
+ USHORT DeviceNameLength;
+ PWCHAR p;
+ PWCHAR FilePart;
+ USHORT FilePartLength;
+
+ if ( LsapAdtLookupDriveLetter( FileName, &DeviceNameLength, &DriveLetter )) {
+
+ p = FileName->Buffer;
+ FilePart = (PWCHAR)((PCHAR)(FileName->Buffer) + DeviceNameLength);
+ FilePartLength = FileName->Length - DeviceNameLength;
+
+
+ *p = DriveLetter;
+ *++p = L':';
+
+ //
+ // THIS IS AN OVERLAPPED COPY! DO NOT USE RTLCOPYMEMORY!
+ //
+
+ RtlMoveMemory( ++p, FilePart, FilePartLength );
+
+ FileName->Length = FilePartLength + 2 * sizeof( WCHAR );
+ }
+}
+
+
+
+BOOLEAN
+LsapAdtLookupDriveLetter(
+ IN PUNICODE_STRING FileName,
+ OUT PUSHORT DeviceNameLength,
+ OUT PWCHAR DriveLetter
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will take a file name and compare it to the
+ list of device names obtained during LSA initialization.
+ If one of the device names matches the prefix of the file
+ name the corresponding drive letter will be returned.
+
+Arguments:
+
+ FileName - Supplies a unicode string containing the file
+ name obtained from the file system.
+
+ DeviceNameLength - If successful, returns the length of
+ the device name.
+
+ DriveLetter - If successful, returns the drive letter
+ corresponding to the device object.
+
+Return Value:
+
+ Returns TRUE of a mapping is found, FALSE otherwise.
+
+--*/
+
+{
+ ULONG i = 0;
+ PUNICODE_STRING DeviceName;
+ USHORT OldLength;
+
+
+ while (DriveMappingArray[i].DeviceName.Buffer != NULL ) {
+
+ DeviceName = &DriveMappingArray[i].DeviceName;
+
+ //
+ // If the device name is longer than the passed file name,
+ // it can't be a match.
+ //
+
+ if ( DeviceName->Length > FileName->Length ) {
+ i++;
+ continue;
+ }
+
+ //
+ // Temporarily truncate the file name to be the same
+ // length as the device name by adjusting the length field
+ // in its unicode string structure. Then compare them and
+ // see if they match.
+ //
+ // The test above ensures that this is a safe thing to
+ // do.
+ //
+
+ OldLength = FileName->Length;
+ FileName->Length = DeviceName->Length;
+
+
+ if ( RtlEqualUnicodeString( FileName, DeviceName, TRUE ) ) {
+
+ //
+ // We've got a match.
+ //
+
+ FileName->Length = OldLength;
+ *DriveLetter = DriveMappingArray[i].DriveLetter;
+ *DeviceNameLength = DeviceName->Length;
+ return( TRUE );
+
+ }
+
+ FileName->Length = OldLength;
+ i++;
+ }
+
+ return( FALSE );
+}
+
+
+VOID
+LsapAuditFailed(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Implements current policy of how to deal with a failed audit.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES Obja;
+ HANDLE KeyHandle;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING ValueName;
+ UCHAR NewValue;
+ ULONG Response;
+
+ ASSERT(sizeof(UCHAR) == sizeof(BOOLEAN));
+
+ if (LsapCrashOnAuditFail) {
+
+ //
+ // Turn off flag in the registry that controls crashing on audit failure
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
+
+ InitializeObjectAttributes( &Obja,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ do {
+
+ Status = NtOpenKey(
+ &KeyHandle,
+ KEY_SET_VALUE,
+ &Obja
+ );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+
+ //
+ // If the LSA key isn't there, he's got big problems. But don't crash.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ LsapCrashOnAuditFail = FALSE;
+ return;
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ goto bugcheck;
+ }
+
+ RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
+
+ NewValue = LSAP_ALLOW_ADIMIN_LOGONS_ONLY;
+
+ do {
+
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_NONE,
+ &NewValue,
+ sizeof(UCHAR)
+ );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+ ASSERT(NT_SUCCESS(Status));
+
+ if (!NT_SUCCESS( Status )) {
+ goto bugcheck;
+ }
+
+ do {
+
+ Status = NtFlushKey( KeyHandle );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ //
+ // go boom.
+ //
+
+bugcheck:
+
+ Status = NtRaiseHardError(
+ STATUS_AUDIT_FAILED,
+ 0,
+ 0,
+ NULL,
+ OptionShutdownSystem,
+ &Response
+ );
+
+}
+
+
diff --git a/private/lsa/server/adtobjs.c b/private/lsa/server/adtobjs.c
new file mode 100644
index 000000000..b5821ec94
--- /dev/null
+++ b/private/lsa/server/adtobjs.c
@@ -0,0 +1,1364 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtobjs.c
+
+Abstract:
+
+ Local Security Authority - Auditing object parameter file services.
+
+Author:
+
+ Jim Kelly (JimK) 20-Oct-1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <msaudite.h>
+#include <msobjs.h>
+#include "lsasrvp.h"
+#include "adtp.h"
+
+
+
+//
+// This is the maximum length of standard access type names.
+// This is used to build an array.
+//
+
+#define ADTP_MAX_ACC_NAME_LENGTH (12)
+
+
+//
+//
+// This module builds a list of event source module descriptors.
+// The source modules are identified by name (kept in the descriptor).
+//
+//
+// For each source module a list of objects exported by that module is
+// linked to the source module's descriptor. Each entry in this list
+// is an object descriptor containing a name and a base event offset
+// for specific access types.
+//
+//
+// The chicken-wire data structure for source module and object descriptors
+// looks like:
+//
+// LsapAdtSourceModules --+
+// |
+// +------------------+
+// |
+// |
+// | +-----------+ +-----------+
+// +--->| Next ----|---------------------------->| Next ----|--->...
+// | | | |
+// |-----------| |-----------|
+// | Name | | Name |
+// | | | |
+// |-----------| |-----------|
+// | Objects | | Objects |
+// | o | | o |
+// +-----o-----+ +-----o-----+
+// o +-------+ +-------+ o
+// o | Next--|->| Next--|->... o
+// ooo>|-------| |-------| oooooo> ...
+// | Name | | Name |
+// |-------| |-------|
+// | Base | | Base |
+// | Offset| | Offset|
+// +-------+ +-------+
+//
+// The specific access type names are expected to have contiguous message IDs
+// starting at the base offset value. For example, the access type name for
+// specific access bit 0 for the framitz object might have message ID 2132
+// (and bit 0 serves as the base offset). So, specific access bit 4 would be
+// message ID (2132+4).
+//
+// The valid mask defines the set of specific accesses defined by each object
+// type. If there are gaps in the valid mask, the arithmetic above must still
+// be ensured. That is, the message ID of the specific access related to
+// bit n is message ID (BaseOffset + bit position). So, for example, if
+// bits 0, 1, 4 and 5 are valid (and 2 & 3 are not), be sure to leave unused
+// message IDs where bits 2 and 3 would normally be.
+//
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Data types used within this module //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+#define LSAP_ADT_ACCESS_NAME_FORMATTING L"\r\n\t\t"
+
+
+#define LsapAdtSourceModuleLock() (RtlEnterCriticalSection(&LsapAdtSourceModuleLock))
+#define LsapAdtSourceModuleUnlock() (RtlLeaveCriticalSection(&LsapAdtSourceModuleLock))
+
+
+
+//
+// Each event source is represented by a source module descriptor.
+// These are kept on a linked list (LsapAdtSourceModules).
+//
+
+typedef struct _LSAP_ADT_OBJECT {
+
+ //
+ // Pointer to next source module descriptor
+ // This is assumed to be the first field in the structure.
+ //
+
+ struct _LSAP_ADT_OBJECT *Next;
+
+ //
+ // Name of object
+ //
+
+ UNICODE_STRING Name;
+
+ //
+ // Base offset of specific access types
+ //
+
+ ULONG BaseOffset;
+
+} LSAP_ADT_OBJECT, *PLSAP_ADT_OBJECT;
+
+
+
+
+//
+// Each event source is represented by a source module descriptor.
+// These are kept on a linked list (LsapAdtSourceModules).
+//
+
+typedef struct _LSAP_ADT_SOURCE {
+
+ //
+ // Pointer to next source module descriptor
+ // This is assumed to be the first field in the structure.
+ //
+
+ struct _LSAP_ADT_SOURCE *Next;
+
+ //
+ // Name of source module
+ //
+
+ UNICODE_STRING Name;
+
+ //
+ // list of objects
+ //
+
+ PLSAP_ADT_OBJECT Objects;
+
+} LSAP_ADT_SOURCE, *PLSAP_ADT_SOURCE;
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Variables global within this module //
+// //
+////////////////////////////////////////////////////////////////////////
+
+//
+// List head for source modules, and lock protecting references
+// or modifications of the links in that list.
+//
+// Once a module's or object's name and value are established, they
+// are never changed. So, this lock only needs to be held while
+// links are being referenced or changed. You don't need to retain
+// it just so you can reference, for example, the name or BaseOffset
+// of an object.
+//
+
+PLSAP_ADT_SOURCE LsapAdtSourceModules;
+RTL_CRITICAL_SECTION LsapAdtSourceModuleLock;
+
+
+
+
+//
+// This is used to house well-known access ID strings.
+// Each string name may be up to ADTP_MAX_ACC_NAME_LENGTH WCHARs long.
+// There are 16 specific names, and 4
+//
+
+ULONG LsapAdtAccessIdsStringBuffer[
+ ADTP_MAX_ACC_NAME_LENGTH * // max wchars in each string
+ (sizeof(ULONG)/sizeof(WCHAR)) // wchars, not ulongs.
+ * 23 // and there are this many
+ ];
+
+
+
+//
+// Well known event ID strings.
+//
+
+UNICODE_STRING LsapAdtEventIdStringDelete,
+ LsapAdtEventIdStringReadControl,
+ LsapAdtEventIdStringWriteDac,
+ LsapAdtEventIdStringWriteOwner,
+ LsapAdtEventIdStringSynchronize,
+ LsapAdtEventIdStringAccessSysSec,
+ LsapAdtEventIdStringMaxAllowed,
+ LsapAdtEventIdStringSpecific[16];
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Services exported by this module. //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapAdtObjsInitialize(
+ )
+
+/*++
+
+Routine Description:
+
+ This function reads the object parameter file information from the
+ registry.
+
+ This service should be called in pass 1.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ to store the object information.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status,
+ IgnoreStatus;
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ HANDLE AuditKey,
+ ModuleKey,
+ ObjectNamesKey;
+
+ ULONG i,
+ ModuleIndex,
+ ObjectIndex,
+ RequiredLength;
+
+ UNICODE_STRING AuditKeyName,
+ TmpString;
+
+ PLSAP_ADT_SOURCE NextModule;
+
+ PKEY_BASIC_INFORMATION KeyInformation;
+
+
+
+ PLSAP_ADT_OBJECT NextObject;
+
+ PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
+
+ PULONG ObjectData;
+
+ BOOLEAN ModuleHasObjects;
+
+
+
+
+
+ //
+ // Initialize module-global variables, including strings we will need
+ //
+
+
+
+ //
+ // List of source modules and objects. These lists are constantly
+ // being adjusted to try to improve performance. Access to these
+ // lists is protected by a critical section.
+ //
+
+ LsapAdtSourceModules = NULL;
+ RtlInitializeCriticalSection(&LsapAdtSourceModuleLock);
+
+
+
+ //
+ // we need a number of strings.
+ //
+
+ i=0;
+ LsapAdtEventIdStringDelete.Length = 0;
+ LsapAdtEventIdStringDelete.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringDelete.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_DELETE,
+ 10, //Base
+ &LsapAdtEventIdStringDelete
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringReadControl.Length = 0;
+ LsapAdtEventIdStringReadControl.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringReadControl.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_READ_CONTROL,
+ 10, //Base
+ &LsapAdtEventIdStringReadControl
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringWriteDac.Length = 0;
+ LsapAdtEventIdStringWriteDac.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringWriteDac.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_WRITE_DAC,
+ 10, //Base
+ &LsapAdtEventIdStringWriteDac
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringWriteOwner.Length = 0;
+ LsapAdtEventIdStringWriteOwner.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringWriteOwner.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_WRITE_OWNER,
+ 10, //Base
+ &LsapAdtEventIdStringWriteOwner
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSynchronize.Length = 0;
+ LsapAdtEventIdStringSynchronize.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSynchronize.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SYNCHRONIZE,
+ 10, //Base
+ &LsapAdtEventIdStringSynchronize
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringAccessSysSec.Length = 0;
+ LsapAdtEventIdStringAccessSysSec.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringAccessSysSec.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_ACCESS_SYS_SEC,
+ 10, //Base
+ &LsapAdtEventIdStringAccessSysSec
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringMaxAllowed.Length = 0;
+ LsapAdtEventIdStringMaxAllowed.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringMaxAllowed.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_MAXIMUM_ALLOWED,
+ 10, //Base
+ &LsapAdtEventIdStringMaxAllowed
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[0].Length = 0;
+ LsapAdtEventIdStringSpecific[0].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[0].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_0,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[0]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[1].Length = 0;
+ LsapAdtEventIdStringSpecific[1].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[1].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_1,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[1]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[2].Length = 0;
+ LsapAdtEventIdStringSpecific[2].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[2].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_2,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[2]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[3].Length = 0;
+ LsapAdtEventIdStringSpecific[3].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[3].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_3,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[3]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[4].Length = 0;
+ LsapAdtEventIdStringSpecific[4].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[4].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_4,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[4]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[5].Length = 0;
+ LsapAdtEventIdStringSpecific[5].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[5].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_5,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[5]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[6].Length = 0;
+ LsapAdtEventIdStringSpecific[6].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[6].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_6,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[6]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[7].Length = 0;
+ LsapAdtEventIdStringSpecific[7].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[7].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_7,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[7]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[8].Length = 0;
+ LsapAdtEventIdStringSpecific[8].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[8].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_8,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[8]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[9].Length = 0;
+ LsapAdtEventIdStringSpecific[9].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[9].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_9,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[9]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[10].Length = 0;
+ LsapAdtEventIdStringSpecific[10].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[10].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_10,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[10]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[11].Length = 0;
+ LsapAdtEventIdStringSpecific[11].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[11].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_11,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[11]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[12].Length = 0;
+ LsapAdtEventIdStringSpecific[12].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[12].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_12,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[12]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[13].Length = 0;
+ LsapAdtEventIdStringSpecific[13].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[13].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_13,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[13]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[14].Length = 0;
+ LsapAdtEventIdStringSpecific[14].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[14].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_14,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[14]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ i+= ADTP_MAX_ACC_NAME_LENGTH / (sizeof(ULONG)/sizeof(WCHAR)); //Skip to the beginning of the next string
+ LsapAdtEventIdStringSpecific[15].Length = 0;
+ LsapAdtEventIdStringSpecific[15].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
+ LsapAdtEventIdStringSpecific[15].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
+ Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_15,
+ 10, //Base
+ &LsapAdtEventIdStringSpecific[15]
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+
+
+
+
+
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("\t\tSE_ADT: Dumping AUDIT SOURCE MODULE\n");
+ printf("\t\t information from registry...\n\n");
+#endif
+
+ //
+ // The modules and their objects are listed in the registry
+ // under the key called LSAP_ADT_AUDIT_MODULES_KEY_NAME.
+ // Open that key.
+ //
+
+ RtlInitUnicodeString( &AuditKeyName, LSAP_ADT_AUDIT_MODULES_KEY_NAME );
+ //RtlInitUnicodeString( &AuditKeyName,
+ // L"\\Registry\\Machine\\System\\CurrentControlSet\\Services" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &AuditKeyName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ NULL
+ );
+ Status = NtOpenKey( &AuditKey, KEY_READ, &ObjectAttributes );
+//printf("first open key: 0x%lx\n", Status);
+
+
+ ModuleIndex = 0;
+ while (NT_SUCCESS(Status)) {
+
+
+ //
+ // Now enumerate the source module keys
+ // First find out how long a buffer we need.
+ //
+
+
+ KeyInformation = NULL;
+ Status = NtEnumerateKey( AuditKey,
+ ModuleIndex,
+ KeyBasicInformation,
+ (PVOID)KeyInformation,
+ 0,
+ &RequiredLength
+ );
+//printf("first enumerate key: 0x%lx\n", Status);
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ KeyInformation = RtlAllocateHeap( RtlProcessHeap(), 0,
+ RequiredLength );
+ if (KeyInformation == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ Status = NtEnumerateKey( AuditKey,
+ ModuleIndex,
+ KeyBasicInformation,
+ (PVOID)KeyInformation,
+ RequiredLength,
+ &RequiredLength
+ );
+//printf(" enumerate key: 0x%lx\n", Status);
+ ModuleIndex ++;
+
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Build a source module descriptor for this key
+ //
+
+ NextModule = RtlAllocateHeap( RtlProcessHeap(), 0,
+ sizeof(LSAP_ADT_SOURCE) );
+ if (NextModule == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ NextModule->Next = LsapAdtSourceModules;
+ LsapAdtSourceModules = NextModule;
+ NextModule->Objects = NULL;
+ NextModule->Name.Length = (USHORT)KeyInformation->NameLength;
+ NextModule->Name.MaximumLength = NextModule->Name.Length + 2;
+ NextModule->Name.Buffer = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ NextModule->Name.MaximumLength );
+ if (NextModule->Name.Buffer == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ TmpString.Length = (USHORT)KeyInformation->NameLength;
+ TmpString.MaximumLength = TmpString.Length;
+ TmpString.Buffer = &KeyInformation->Name[0];
+ RtlCopyUnicodeString( &NextModule->Name, &TmpString );
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("\n\n\tSource Module:\t%wS\n", &NextModule->Name);
+#endif
+
+ RtlFreeHeap( RtlProcessHeap(), 0, KeyInformation );
+
+ //
+ // Now open that source module's "\ObjectNames" sub-key
+ //
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &NextModule->Name,
+ OBJ_CASE_INSENSITIVE,
+ AuditKey,
+ NULL );
+
+ Status = NtOpenKey( &ModuleKey, KEY_READ, &ObjectAttributes );
+//printf("Open Source Mod key: 0x%lx\n", Status);
+
+ if (!NT_SUCCESS(Status)) {
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("LSAP AUDIT: Can't open audit module key: %wS\n",
+ &NextModule->Name);
+#endif
+ return(Status);
+ }
+
+ RtlInitUnicodeString( &TmpString, LSAP_ADT_OBJECT_NAMES_KEY_NAME);
+ InitializeObjectAttributes( &ObjectAttributes,
+ &TmpString,
+ OBJ_CASE_INSENSITIVE,
+ ModuleKey,
+ NULL );
+
+ Status = NtOpenKey( &ObjectNamesKey, KEY_READ, &ObjectAttributes );
+//printf(" Part 2: 0x%lx\n", Status);
+ IgnoreStatus = NtClose( ModuleKey ); ASSERT(NT_SUCCESS(IgnoreStatus));
+ ModuleHasObjects = TRUE;
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ ModuleHasObjects = FALSE;
+ Status = STATUS_SUCCESS;
+ }
+
+ }
+ }
+
+
+
+
+
+ //
+ // At this point we have either:
+ //
+ // 1) Found a source module with objects under it
+ // that need to be retrieved.
+ // This is indicated by successful status value and
+ // (ModuleHasObjects == TRUE).
+ //
+ // 2) found a source module with no objects under it,
+ // This is indicated by (ModuleHasObjects == FALSE)
+ //
+ // 3) exhausted our source modules enumeration,
+ //
+ // 4) hit another type of error, or
+ //
+ // (3) and (4) are indicatd by non-successful status values.
+ //
+ // In the case of (1) or (2) , NextModule points to the module we
+ // are working on. For case (1), ObjectNamesKey is the handle to
+ // the \ObjectNames registry key for the source module.
+ //
+
+ ObjectIndex = 0;
+ while ( NT_SUCCESS(Status) && (ModuleHasObjects == TRUE)) {
+
+
+ //
+ // Now enumerate the objects (i.e., values) of this
+ // source module.
+ //
+
+ KeyValueInformation = NULL;
+ Status = NtEnumerateValueKey( ObjectNamesKey,
+ ObjectIndex,
+ KeyValueFullInformation,
+ KeyValueInformation,
+ 0,
+ &RequiredLength
+ );
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ KeyValueInformation = RtlAllocateHeap( RtlProcessHeap(), 0,
+ RequiredLength );
+ if (KeyValueInformation == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ Status = NtEnumerateValueKey( ObjectNamesKey,
+ ObjectIndex,
+ KeyValueFullInformation,
+ KeyValueInformation,
+ RequiredLength,
+ &RequiredLength
+ );
+ ObjectIndex ++;
+
+
+
+ if (NT_SUCCESS(Status)) {
+
+
+ //
+ // Build an object descriptor for the object represented
+ // by this object.
+ //
+
+ NextObject = RtlAllocateHeap( RtlProcessHeap(), 0,
+ sizeof(LSAP_ADT_OBJECT) );
+ if (NextObject == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ NextObject->Next = NextModule->Objects;
+ NextModule->Objects = NextObject;
+ NextObject->Name.Length = (USHORT)KeyValueInformation->NameLength;
+ NextObject->Name.MaximumLength = NextObject->Name.Length + 2;
+ NextObject->Name.Buffer = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ NextObject->Name.MaximumLength );
+ if (NextObject->Name.Buffer == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ TmpString.Length = (USHORT)KeyValueInformation->NameLength;
+ TmpString.MaximumLength = TmpString.Length;
+ TmpString.Buffer = &KeyValueInformation->Name[0];
+ RtlCopyUnicodeString( &NextObject->Name, &TmpString );
+
+
+ if (KeyValueInformation->DataLength < sizeof(ULONG)) {
+ NextObject->BaseOffset = SE_ACCESS_NAME_SPECIFIC_0;
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("\t\tERROR: Following object has invalid Message ID \n");
+ printf("\t\t offset in registry. Substituting generic\n");
+ printf("\t\t access names. The registry contents should\n");
+ printf("\t\t be corrected by an administrator.\n");
+#endif
+ } else {
+ ObjectData = (PVOID)(((PUCHAR)KeyValueInformation) +
+ KeyValueInformation->DataOffset);
+ NextObject->BaseOffset = (*ObjectData);
+ }
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("\t\tObject Type:\t%wS\n", &NextObject->Name);
+ printf("\t\t\t\tMessageID: 0x%lx\n", NextObject->BaseOffset);
+#endif
+
+ } //end_if (buffer read)
+
+ RtlFreeHeap( RtlProcessHeap(), 0, KeyValueInformation );
+
+ } //end_if (buffer overflowed)
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+ Status = STATUS_SUCCESS;
+ ModuleHasObjects = FALSE;
+ }
+
+ } //end_while (enumerate values)
+
+
+
+ } //end_while (enumerate modules)
+
+ //
+ // If we were successful, then we will probably have a
+ // current completion status of STATUS_NO_MORE_ENTRIES
+ // (indicating our enumerations above were run). Change
+ // this to success.
+ //
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+ Status = STATUS_SUCCESS;
+ }
+
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ {
+ PLSAP_ADT_SOURCE source;
+ PLSAP_ADT_OBJECT object;
+
+ printf("Dump of database:");
+
+ source = LsapAdtSourceModules;
+ while (source) {
+ printf("\n\n\tModule: %wS\n", &source->Name);
+
+ object = source->Objects;
+ while (object) {
+ printf("\t\tObject: \n\t\t\t%d\n\t\t\t%wS\n",
+ object->BaseOffset, &object->Name);
+ object = object->Next;
+
+ }
+
+ source = source->Next;
+
+ }
+
+ }
+#endif
+
+
+ return(Status);
+
+}
+
+
+
+
+NTSTATUS
+LsapAdtBuildAccessesString(
+ IN PUNICODE_STRING SourceModule,
+ IN PUNICODE_STRING ObjectTypeName,
+ IN ACCESS_MASK Accesses,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a unicode string containing parameter
+ file replacement parameters (e.g. %%1043) separated by carriage
+ return and tab characters suitable for display via the event viewer.
+
+
+ The buffer returned by this routine must be deallocated when no
+ longer needed if FreeWhenDone is true.
+
+
+ NOTE: To enhance performance, each time a target source module
+ descriptor is found, it is moved to the beginning of the
+ source module list. This ensures frequently accessed source
+ modules are always near the front of the list.
+
+ Similarly, target object descriptors are moved to the front
+ of their lists when found. This further ensures high performance
+ by quicly locating
+
+
+
+Arguments:
+
+ SourceModule - The module (ala event viewer modules) defining the
+ object type.
+
+ ObjectTypeName - The type of object to which the access mask applies.
+
+ Accesses - The access mask to be used in building the display string.
+
+ ResultantString - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+
+ FreeWhenDone - If TRUE, indicates that the body of the ResultantString
+ must be freed to process heap when no longer needed.
+
+Return Values:
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ to store the object information.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG AccessCount = 0;
+ ULONG BaseOffset;
+ ULONG i;
+ ACCESS_MASK Mask;
+ PLSAP_ADT_SOURCE Source;
+ PLSAP_ADT_SOURCE FoundSource;
+ PLSAP_ADT_OBJECT Object;
+ PLSAP_ADT_OBJECT FoundObject;
+ BOOLEAN Found;
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("Module:\t%wS\n", SourceModule);
+ printf("\t Object:\t%wS\n", ObjectTypeName);
+ printf("\t Accesses:\t0x%lx\n", Accesses);
+#endif
+
+ //
+ // If we have no accesses, return "-"
+ //
+
+ if (Accesses == 0) {
+
+ RtlInitUnicodeString( ResultantString, L"-" );
+ (*FreeWhenDone) = FALSE;
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // First figure out how large a buffer we need
+ //
+
+ Mask = Accesses;
+
+ //
+ // Count the number of set bits in the
+ // passed access mask.
+ //
+
+ while ( Mask != 0 ) {
+ Mask = Mask & (Mask - 1);
+ AccessCount++;
+ }
+
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("\t \t%d bits set in mask.\n", AccessCount);
+#endif
+
+
+ //
+ // We have accesses, allocate a string large enough to deal
+ // with them all. Strings will be of the format:
+ //
+ // %%nnnnnnnnnn\n\r\t\t%%nnnnnnnnnn\n\r\t\t ... %nnnnnnnnnn\n\r\t\t
+ //
+ // where nnnnnnnnnn - is a decimal number 10 digits long or less.
+ //
+ // So, a typical string will look like:
+ //
+ // %%601\n\r\t\t%%1604\n\r\t\t%%1608\n
+ //
+ // Since each such access may use at most:
+ //
+ // 10 (for the nnnnnnnnnn digit)
+ // + 2 (for %%)
+ // + 8 (for \n\t\t)
+ // --------------------------------
+ // 20 wide characters
+ //
+ // The total length of the output string will be:
+ //
+ // AccessCount (number of accesses)
+ // x 20 (size of each entry)
+ // -------------------------------------
+ // wchars
+ //
+ // Throw in 1 more WCHAR for null termination, and we are all set.
+ //
+
+ ResultantString->Length = 0;
+ ResultantString->MaximumLength = (USHORT)AccessCount * (20 * sizeof(WCHAR)) +
+ sizeof(WCHAR); //for the null termination
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+ printf("\t \t%d byte buffer allocated.\n", ResultantString->MaximumLength);
+#endif
+ ResultantString->Buffer = LsapAllocateLsaHeap( ResultantString->MaximumLength );
+
+
+ if (ResultantString->Buffer == NULL) {
+
+ return(STATUS_NO_MEMORY);
+ }
+
+ (*FreeWhenDone) = TRUE;
+
+ //
+ // Special case standard and special access types.
+ // Walk the lists for specific access types.
+ //
+
+ if (Accesses & STANDARD_RIGHTS_ALL) {
+
+ if (Accesses & DELETE) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringDelete);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+
+ if (Accesses & READ_CONTROL) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringReadControl);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+
+ if (Accesses & WRITE_DAC) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringWriteDac);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+
+ if (Accesses & WRITE_OWNER) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringWriteOwner);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+ if (Accesses & SYNCHRONIZE) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringSynchronize);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+ }
+
+
+ if (Accesses & ACCESS_SYSTEM_SECURITY) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringAccessSysSec);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+ if (Accesses & MAXIMUM_ALLOWED) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringMaxAllowed);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+
+ //
+ // If there are any specific access bits set, then get
+ // the appropriate source module and object type base
+ // message ID offset. If there is no module-specific
+ // object definition, then use SE_ACCESS_NAME_SPECIFIC_0
+ // as the base.
+ //
+
+ if ((Accesses & SPECIFIC_RIGHTS_ALL) == 0) {
+ return(Status);
+ }
+
+ LsapAdtSourceModuleLock();
+
+ Source = (PLSAP_ADT_SOURCE)&LsapAdtSourceModules;
+ Found = FALSE;
+
+ while ((Source->Next != NULL) && !Found) {
+
+ if (RtlEqualUnicodeString(&Source->Next->Name, SourceModule, TRUE)) {
+
+ Found = TRUE;
+ FoundSource = Source->Next;
+
+ //
+ // Move to front of list of source modules.
+ //
+
+ Source->Next = FoundSource->Next; // Remove from list
+ FoundSource->Next = LsapAdtSourceModules; // point to first element
+ LsapAdtSourceModules = FoundSource; // Make it the first element
+
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+printf("\t \tModule Found.\n");
+#endif
+
+ } else {
+
+ Source = Source->Next;
+ }
+ }
+
+
+ if (Found == TRUE) {
+
+ //
+ // Find the object
+ //
+
+ Object = (PLSAP_ADT_OBJECT)&(FoundSource->Objects);
+ Found = FALSE;
+
+ while ((Object->Next != NULL) && !Found) {
+
+ if (RtlEqualUnicodeString(&Object->Next->Name, ObjectTypeName, TRUE)) {
+
+ Found = TRUE;
+ FoundObject = Object->Next;
+
+ //
+ // Move to front of list of soure modules.
+ //
+
+ Object->Next = FoundObject->Next; // Remove from list
+ FoundObject->Next = FoundSource->Objects; // point to first element
+ FoundSource->Objects = FoundObject; // Make it the first element
+
+ } else {
+
+ Object = Object->Next;
+ }
+ }
+ }
+
+
+ //
+ // We are done playing with link fields of the source modules
+ // and objects. Free the lock.
+ //
+
+ LsapAdtSourceModuleUnlock();
+
+ //
+ // If we have found an object, use it as our base message
+ // ID. Otherwise, use SE_ACCESS_NAME_SPECIFIC_0.
+ //
+
+ if (Found) {
+
+ BaseOffset = FoundObject->BaseOffset;
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+printf("\t \tObject Found. Base Offset: 0x%lx\n", BaseOffset);
+#endif
+
+ } else {
+
+ BaseOffset = SE_ACCESS_NAME_SPECIFIC_0;
+#ifdef LSAP_ADT_TEST_DUMP_SOURCES
+printf("\t \tObject NOT Found. Base Offset: 0x%lx\n", BaseOffset);
+#endif
+ }
+
+
+ //
+ // At this point, we have a base offset (even if we had to use our
+ // default).
+ //
+ // Now cycle through the specific access bits and see which ones need
+ // to be added to ResultantString.
+ //
+
+ {
+ UNICODE_STRING IntegerString;
+ WCHAR IntegerStringBuffer[10]; //must be 10 wchar bytes long
+ ULONG NextBit;
+
+ IntegerString.Buffer = (PWSTR)IntegerStringBuffer;
+ IntegerString.MaximumLength = 10*sizeof(WCHAR);
+ IntegerString.Length = 0;
+
+ for ( i=0, NextBit=1 ; i<16 ; i++, NextBit <<= 1 ) {
+
+ //
+ // specific access flags are in the low-order bits of the mask
+ //
+
+ if ((NextBit & Accesses) != 0) {
+
+ //
+ // Found one - add it to ResultantString
+ //
+
+ Status = RtlIntegerToUnicodeString (
+ (BaseOffset + i),
+ 10, //Base
+ &IntegerString
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeStringToString( ResultantString, &IntegerString);
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
+ ASSERT( NT_SUCCESS( Status ));
+ }
+ }
+ }
+ }
+
+ return(Status);
+
+
+//ErrorAfterAlloc:
+//
+// LsapFreeLsaHeap( ResultantString->Buffer );
+// ResultantString->Buffer = NULL;
+// (*FreeWhenDone) = FALSE;
+// return(Status);
+}
diff --git a/private/lsa/server/adtp.h b/private/lsa/server/adtp.h
new file mode 100644
index 000000000..224c290fe
--- /dev/null
+++ b/private/lsa/server/adtp.h
@@ -0,0 +1,493 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtp.h
+
+Abstract:
+
+ Local Security Authority - Audit Log Management - Private Defines,
+ data and function prototypes.
+
+ Functions, data and defines in this module are internal to the
+ Auditing Subcomponent of the LSA Subsystem.
+
+Author:
+
+ Scott Birrell (ScottBi) November 20, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LSAP_ADTP_
+#define _LSAP_ADTP_
+
+
+#include "ausrvp.h"
+
+
+//
+// Names of the registry keys where security event log information
+// is rooted and the object names are listed under an event source
+// module.
+//
+
+#define LSAP_ADT_AUDIT_MODULES_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\EventLog\\Security"
+#define LSAP_ADT_OBJECT_NAMES_KEY_NAME L"ObjectNames"
+
+
+
+
+//
+// Macros for setting fields in an SE_AUDIT_PARAMETERS array.
+//
+// These must be kept in sync with similar macros in se\sepaudit.c.
+//
+
+
+#define LsapSetParmTypeSid( AuditParameters, Index, Sid ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeSid; \
+ (AuditParameters).Parameters[(Index)].Length = RtlLengthSid( (Sid) ); \
+ (AuditParameters).Parameters[(Index)].Address = (Sid); \
+ }
+
+
+
+#define LsapSetParmTypeString( AuditParameters, Index, String ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeString; \
+ (AuditParameters).Parameters[(Index)].Length = \
+ sizeof(UNICODE_STRING)+(String)->Length; \
+ (AuditParameters).Parameters[(Index)].Address = (String); \
+ }
+
+
+
+#define LsapSetParmTypeUlong( AuditParameters, Index, Ulong ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeUlong; \
+ (AuditParameters).Parameters[(Index)].Length = sizeof( (Ulong) ); \
+ (AuditParameters).Parameters[(Index)].Data[0] = (ULONG)(Ulong); \
+ }
+
+
+#define LsapSetParmTypeNoLogon( AuditParameters, Index ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeNoLogonId; \
+ }
+
+
+
+#define LsapSetParmTypeLogonId( AuditParameters, Index, LogonId ) \
+ { \
+ PLUID TmpLuid; \
+ \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeLogonId; \
+ (AuditParameters).Parameters[(Index)].Length = sizeof( (LogonId) ); \
+ TmpLuid = (PLUID)(&(AuditParameters).Parameters[(Index)].Data[0]); \
+ *TmpLuid = (LogonId); \
+ }
+
+
+#define LsapSetParmTypePrivileges( AuditParameters, Index, Privileges ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypePrivs; \
+ (AuditParameters).Parameters[(Index)].Length = LsapPrivilegeSetSize( (Privileges) ); \
+ (AuditParameters).Parameters[(Index)].Address = (Privileges); \
+ }
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Private data for Audit Log Management //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+#define LSAP_ADT_LOG_FULL_SHUTDOWN_TIMEOUT (ULONG) 0x0000012cL
+
+extern RTL_CRITICAL_SECTION LsapAdtQueueLock;
+extern RTL_CRITICAL_SECTION LsapAdtLogFullLock;
+
+extern BOOLEAN LsapAuditSuccessfulLogons;
+
+extern BOOLEAN LsapAuditFailedLogons;
+
+//
+// Options for LsapAdtWriteLog
+//
+
+#define LSAP_ADT_LOG_QUEUE_PREPEND ((ULONG) 0x00000001L)
+
+//
+// Structure describing a queued audit record
+//
+
+typedef struct _LSAP_ADT_QUEUED_RECORD {
+
+ struct _LSAP_ADT_QUEUED_RECORD *Next;
+ SE_ADT_PARAMETER_ARRAY Buffer;
+
+} LSAP_ADT_QUEUED_RECORD, *PLSAP_ADT_QUEUED_RECORD;
+
+//
+// Audit Log Queue Header. The queue is maintained in chronological
+// (FIFO) order. New records are appended to the back of the queue.
+//
+
+typedef struct _LSAP_ADT_LOG_QUEUE_HEAD {
+
+ PLSAP_ADT_QUEUED_RECORD FirstQueuedRecord;
+ PLSAP_ADT_QUEUED_RECORD LastQueuedRecord;
+
+} LSAP_ADT_LOG_QUEUE_HEAD, *PLSAP_ADT_LOG_QUEUE_HEAD;
+
+extern LSAP_ADT_LOG_QUEUE_HEAD LsapAdtLogQueue;
+
+//
+// Lsa Global flag to indicate if we are auditing logon events.
+//
+
+extern BOOLEAN LsapAdtLogonEvents;
+
+//
+// String that will be passed in for SubsystemName for audits generated
+// by LSA (eg, logon, logoff, restart, etc).
+//
+
+extern UNICODE_STRING LsapSubsystemName;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// /
+// The following structures and data are used by LSA to contain /
+// drive letter-device name mapping information. LSA obtains this /
+// information once during initialization and saves it for use /
+// by auditing code. /
+// /
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// /
+// The DRIVE_MAPPING structure contains the drive letter (without /
+// the colon) and a unicode string containing the name of the /
+// corresponding device. The buffer in the unicode string is /
+// allocated from the LSA heap and is never freed. /
+// /
+///////////////////////////////////////////////////////////////////////////////
+
+
+typedef struct _DRIVE_MAPPING {
+ WCHAR DriveLetter;
+ UNICODE_STRING DeviceName;
+} DRIVE_MAPPING, PDRIVE_MAPPING;
+
+
+////////////////////////////////////////////////////////////////////////////////
+// /
+// We assume a maximum of 26 drive letters. Though no auditing /
+// will occur due to references to files on floppy (drives A and /
+// B), perform their name lookup anyway. This will then just /
+// work if somehow we start auditing files on floppies. /
+// /
+////////////////////////////////////////////////////////////////////////////////
+
+#define MAX_DRIVE_MAPPING 26
+
+extern DRIVE_MAPPING DriveMappingArray[];
+
+//
+// Special privilege values which are not normally audited,
+// but generate audits when assigned to a user. See
+// LsapAdtAuditSpecialPrivileges.
+//
+
+extern LUID ChangeNotifyPrivilege;
+extern LUID AuditPrivilege;
+extern LUID CreateTokenPrivilege;
+extern LUID AssignPrimaryTokenPrivilege;
+extern LUID BackupPrivilege;
+extern LUID RestorePrivilege;
+extern LUID DebugPrivilege;
+
+//
+// Global variable to indicate whether or not we're
+// supposed to crash when an audit fails.
+//
+
+extern BOOLEAN LsapCrashOnAuditFail;
+extern BOOLEAN LsapAllowAdminLogonsOnly;
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// /
+// /
+////////////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapAdtWriteLog(
+ IN OPTIONAL PSE_ADT_PARAMETER_ARRAY AuditRecord,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapAdtClearLog(
+ );
+
+
+NTSTATUS
+LsapAdtDemarshallAuditInfo(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters
+ );
+
+VOID
+LsapAdtNormalizeAuditInfo(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters
+ );
+
+NTSTATUS
+LsapAdtOpenLog(
+ OUT PHANDLE AuditLogHandle
+ );
+
+NTSTATUS
+LsapAdtLogQueuedEvents(
+ IN ULONG Options
+ );
+
+
+VOID
+LsapAdtQueueLogonEvent(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PUNICODE_STRING AccountName,
+ IN PUNICODE_STRING AuthenticatingAuthority,
+ IN PUNICODE_STRING Source,
+ IN PUNICODE_STRING SourceDevice,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PSID UserSid,
+ IN LUID AuthenticationId,
+ IN NTSTATUS LogonStatus
+ );
+
+VOID
+LsapAdtAuditLogon(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PUNICODE_STRING AccountName,
+ IN PUNICODE_STRING AuthenticatingAuthority,
+ IN PUNICODE_STRING Source,
+ IN PUNICODE_STRING SourceDevice,
+ IN PUNICODE_STRING PackageName,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PSID UserSid,
+ IN LUID AuthenticationId,
+ IN NTSTATUS LogonStatus,
+ IN PUNICODE_STRING WorkstationName
+ );
+
+#define LSAP_ADT_LOG_QUEUE_DISCARD ((ULONG) 0x00000001L)
+#define LSAP_ADT_LOG_QUEUE_WRITEOUT ((ULONG) 0x00000002L)
+
+
+VOID
+LsapAdtSystemRestart(
+ PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo
+ );
+
+VOID
+LsapAdtAuditLogonProcessRegistration(
+ IN PLSAP_AU_REGISTER_CONNECT_INFO ConnectInfo
+ );
+
+
+NTSTATUS
+LsapAdtInitializeLogQueue(
+ VOID
+ );
+
+NTSTATUS
+LsapAdtQueueRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditRecord,
+ IN ULONG Options
+ );
+
+
+NTSTATUS
+LsapAdtSignalLogFull(
+ VOID
+ );
+
+
+NTSTATUS
+LsapAdtAcquireLogQueueLock(
+ VOID
+ );
+
+
+VOID
+LsapAdtReleaseLogQueueLock(
+ VOID
+ );
+
+
+NTSTATUS
+LsapAdtObjsInitialize(
+ );
+
+
+
+NTSTATUS
+LsapAdtBuildDashString(
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ );
+
+NTSTATUS
+LsapAdtBuildUlongString(
+ IN ULONG Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ );
+
+
+NTSTATUS
+LsapAdtBuildLuidString(
+ IN PLUID Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ );
+
+
+NTSTATUS
+LsapAdtBuildSidString(
+ IN PSID Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ );
+
+NTSTATUS
+LsapAdtBuildAccessesString(
+ IN PUNICODE_STRING SourceModule,
+ IN PUNICODE_STRING ObjectTypeName,
+ IN ACCESS_MASK Accesses,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ );
+
+NTSTATUS
+LsapAdtBuildFilePathString(
+ IN PUNICODE_STRING Value,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ );
+
+NTSTATUS
+LsapAdtBuildLogonIdStrings(
+ IN PLUID LogonId,
+ OUT PUNICODE_STRING ResultantString1,
+ OUT PBOOLEAN FreeWhenDone1,
+ OUT PUNICODE_STRING ResultantString2,
+ OUT PBOOLEAN FreeWhenDone2,
+ OUT PUNICODE_STRING ResultantString3,
+ OUT PBOOLEAN FreeWhenDone3
+ );
+
+NTSTATUS
+LsapBuildPrivilegeAuditString(
+ IN PPRIVILEGE_SET PrivilegeSet,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ );
+
+
+NTSTATUS
+LsapAdtMarshallAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
+ OUT PSE_ADT_PARAMETER_ARRAY *MarshalledAuditParameters
+ );
+
+
+VOID
+LsapAdtInitializeDriveLetters(
+ VOID
+ );
+
+
+BOOLEAN
+LsapAdtLookupDriveLetter(
+ IN PUNICODE_STRING FileName,
+ OUT PUSHORT DeviceNameLength,
+ OUT PWCHAR DriveLetter
+ );
+
+VOID
+LsapAdtSubstituteDriveLetter(
+ IN PUNICODE_STRING FileName
+ );
+
+VOID
+LsapAdtUserRightAssigned(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PSID UserSid,
+ IN LUID CallerAuthenticationId,
+ IN PSID ClientSid,
+ IN PPRIVILEGE_SET Privileges
+ );
+
+VOID
+LsapAdtTrustedDomain(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PSID ClientSid,
+ IN LUID CallerAuthenticationId,
+ IN PSID TargetSid,
+ IN PUNICODE_STRING DomainName
+ );
+
+VOID
+LsapAdtAuditLogoff(
+ PLSAP_LOGON_SESSION Session
+ );
+
+VOID
+LsapAdtPolicyChange(
+ IN USHORT EventCategory,
+ IN ULONG EventID,
+ IN USHORT EventType,
+ IN PSID ClientSid,
+ IN LUID CallerAuthenticationId,
+ IN PLSARM_POLICY_AUDIT_EVENTS_INFO LsapAdtEventsInformation
+ );
+
+VOID
+LsapAdtAuditSpecialPrivileges(
+ PPRIVILEGE_SET Privileges,
+ LUID LogonId,
+ PSID UserSid
+ );
+
+VOID
+LsapAuditFailed(
+ VOID
+ );
+
+
+#endif // _LSAP_ADTP_
diff --git a/private/lsa/server/au.h b/private/lsa/server/au.h
new file mode 100644
index 000000000..4a2e5369e
--- /dev/null
+++ b/private/lsa/server/au.h
@@ -0,0 +1,28 @@
+
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ au.h
+
+Abstract:
+
+ LSA Authentication - Exported Function Definitions, Datatypes and Defines
+
+ This module contains the LSA Authentication Routines that may be called
+ by parts of the LSA outside the Authentication sub-component.
+
+Author:
+
+ Scott Birrell (ScottBi) March 24, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+NTSTATUS
+LsapAuOpenSam( VOID );
diff --git a/private/lsa/server/auclient.c b/private/lsa/server/auclient.c
new file mode 100644
index 000000000..9fc0ebd5f
--- /dev/null
+++ b/private/lsa/server/auclient.c
@@ -0,0 +1,268 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ auclient.c
+
+Abstract:
+
+ This module provides client process buffer reference services.
+
+Author:
+
+ Jim Kelly (JimK) 26-February-1991
+
+Revision History:
+
+--*/
+
+#include "ausrvp.h"
+
+
+NTSTATUS
+LsapAllocateClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG LengthRequired,
+ OUT PVOID *ClientBaseAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used to allocate a buffer in a client
+ process.
+
+ Note that this buffer must later be free either by calling
+ LsapFreeClientBuffer(), or by the client calling
+ LsaFreeReturnBuffer().
+
+
+Arguments:
+
+ ClientRequest - Is a pointer to a data structure representing the
+ client process.
+
+ LengthRequired - Indicates the length of buffer (in bytes)
+ needed.
+
+ ClientBaseAddress - Receives the address of the buffer. This
+ address is the virtual address of the buffer within the
+ client process, not in the current process.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_QUOTA_EXCEEDED - Indicates the client process does not
+ have adequate memory quota to allocate the buffer.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG Length;
+ PLSAP_CLIENT_REQUEST IClientRequest;
+
+ //
+ // Typecast to the opaque type
+ //
+
+ IClientRequest = (PLSAP_CLIENT_REQUEST)ClientRequest;
+
+ (*ClientBaseAddress) = NULL;
+ Length = LengthRequired;
+ Status = NtAllocateVirtualMemory(
+ IClientRequest->LogonProcessContext->ClientProcess,
+ ClientBaseAddress,
+ 0,
+ &Length,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapFreeClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ClientBaseAddress OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used to free a buffer previously allocated in a client
+ process.
+
+Arguments:
+
+ ClientRequest - Is a pointer to a data structure representing the
+ client process.
+
+ ClientBaseAddress - Specifies the address of the buffer to free.
+ This address is the virtual address of the buffer within the
+ client process, not in the current process. If specified as
+ NULL, then no deallocation is performed.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ ULONG Length;
+ PLSAP_CLIENT_REQUEST IClientRequest;
+
+ if (ClientBaseAddress == NULL) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Typecast to the opaque type
+ //
+
+ IClientRequest = (PLSAP_CLIENT_REQUEST)ClientRequest;
+
+
+ Length = 0;
+ Status = NtFreeVirtualMemory(
+ IClientRequest->LogonProcessContext->ClientProcess,
+ &ClientBaseAddress,
+ &Length,
+ MEM_RELEASE
+ );
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapCopyToClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG Length,
+ IN PVOID ClientBaseAddress,
+ IN PVOID BufferToCopy
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used to copy information into a client process's address
+ space.
+
+Arguments:
+
+ ClientRequest - Is a pointer to a data structure representing the
+ client process.
+
+ Length - Indicates the length of the buffer (in bytes) to be
+ copied.
+
+ ClientBaseAddress - Is the address of the buffer to receive the
+ data. This address is the address of the buffer within the
+ client process, not in the current process.
+
+ BufferToCopy - Points to the local buffer whose contents are to
+ be copied into the client address space.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PLSAP_CLIENT_REQUEST IClientRequest;
+
+ //
+ // Typecast to the opaque type
+ //
+
+ IClientRequest = (PLSAP_CLIENT_REQUEST)ClientRequest;
+
+ Status = NtWriteVirtualMemory(
+ IClientRequest->LogonProcessContext->ClientProcess,
+ ClientBaseAddress,
+ BufferToCopy,
+ Length,
+ NULL
+ );
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapCopyFromClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG Length,
+ IN PVOID BufferToCopy,
+ IN PVOID ClientBaseAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used to copy information from a client process's address
+ space into a local buffer.
+
+Arguments:
+
+ ClientRequest - Is a pointer to a data structure representing the
+ client process.
+
+ Length - Indicates the length of the buffer (in bytes) to be
+ copied.
+
+ BufferToCopy - Points to the local buffer into which the data is
+ to be copied.
+
+ ClientBaseAddress - Is the address of the client buffer whose
+ contents are to be copied. This address is the address of
+ the buffer within the client process, not in the current
+ process.
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_CLIENT_REQUEST IClientRequest;
+
+ //
+ // Typecast to the opaque type
+ //
+
+ IClientRequest = (PLSAP_CLIENT_REQUEST)ClientRequest;
+
+
+ Status = NtReadVirtualMemory(
+ IClientRequest->LogonProcessContext->ClientProcess,
+ ClientBaseAddress,
+ BufferToCopy,
+ Length,
+ NULL
+ );
+
+ return Status;
+
+}
diff --git a/private/lsa/server/aucred.c b/private/lsa/server/aucred.c
new file mode 100644
index 000000000..b62fc5c7f
--- /dev/null
+++ b/private/lsa/server/aucred.c
@@ -0,0 +1,840 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ aucred.c
+
+Abstract:
+
+ This module provides credential management services within the
+ LSA subsystem. Some of these services are indirectly available for use
+ by authentication packages.
+
+Author:
+
+ Jim Kelly (JimK) 27-February-1991
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "ausrvp.h"
+
+
+
+NTSTATUS
+LsapAddCredential(
+ IN PLUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN PSTRING PrimaryKeyValue,
+ IN PSTRING Credentials
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used by authentication packages to add credentials to a
+ logon session. These credentials may later be referenced using
+ GetCredentials().
+
+ This service acquires the AuLock.
+
+Arguments:
+
+ LogonId - The session ID of logon session to add credentials to.
+
+ AuthenticationPackage - The authentication package ID of the
+ calling authentication package. This was received in the
+ InitializePackage() call during DLL initialization.
+
+ PrimaryKeyValue - Points to a string containing a value that the
+ authentication package will later want to reference as a
+ primary key of the credential data. This may be used, for
+ example, to keep the name of the domain or server the
+ credentials are related to. The format and meaning of this
+ string are authentication package-specific. Note that the
+ string value does not have to be unique, even for the
+ specified logon session. For example, there could be two
+ passwords for the same domain, each with the passwords stored
+ as credentials and the domain name stored as the primary key.
+
+ Credentials - Points to a string containing data representing
+ user credentials. The format and meaning of this string are
+ authentication package-specific.
+
+Return Status:
+
+ STATUS_SUCCESS - The credentials were successfully added.
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
+ not be found.
+
+--*/
+
+{
+
+ PLSAP_LOGON_SESSION LogonSession;
+ PLSAP_PACKAGE_CREDENTIALS Package;
+ PLSAP_CREDENTIALS NewCredentials;
+
+
+ LsapAuLock();
+
+ //
+ // Get a pointer to the logon session
+ //
+
+ LogonSession = LsapGetLogonSession( LogonId, FALSE );
+
+ if ( LogonSession == NULL ) {
+ LsapAuUnlock();
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+
+ //
+ // Now get a pointer to the Package's credentials
+ // (create one if necessary)
+ //
+
+ Package = LsapGetPackageCredentials(
+ LogonSession,
+ AuthenticationPackage,
+ TRUE
+ );
+
+ //
+ // Allocate blocks needed to represent this credential
+ // and copy the primary key and credential strings.
+ //
+
+ NewCredentials = LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_CREDENTIALS) );
+
+ NewCredentials->PrimaryKey.Buffer =
+ (PCHAR)LsapAllocateLsaHeap( (PrimaryKeyValue->Length+1) );
+
+ NewCredentials->PrimaryKey.MaximumLength = PrimaryKeyValue->Length+1;
+
+ RtlCopyString( &NewCredentials->PrimaryKey, PrimaryKeyValue );
+
+ NewCredentials->Credentials.Buffer =
+ (PCHAR)LsapAllocateLsaHeap( (Credentials->Length+1) );
+
+ NewCredentials->Credentials.MaximumLength = Credentials->Length+1;
+
+ RtlCopyString( &NewCredentials->Credentials, Credentials );
+
+ NewCredentials->NextCredentials = Package->Credentials;
+
+ Package->Credentials = NewCredentials;
+
+ LsapAuUnlock();
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+LsapGetCredentials(
+ IN PLUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN OUT PULONG QueryContext,
+ IN BOOLEAN RetrieveAllCredentials,
+ IN PSTRING PrimaryKeyValue,
+ OUT PULONG PrimaryKeyLength,
+ IN PSTRING Credentials
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used by authentication packages to retrieve credentials
+ associated with a logon session. It is expected that each authentication
+ package will provide its own version of this service to its "clients".
+ For example, the MSV1_0 authentication package will provide services for
+ the LM Redirector to retrieve credentials (and probably establish them)
+ for remote accesses. These authentication package level services may be
+ implemented using the LsaCallAuthenticationPackage() API.
+
+ This service acquires the AuLock.
+
+Arguments:
+
+ LogonId - The session ID of logon session from which credentials
+ are to be retrieved.
+
+ AuthenticationPackage - The authentication package ID of the
+ calling authentication package. Authentication packages
+ should only retrieve their own credentials.
+
+ QueryContext - A context value used across successive calls to
+ retrieve multiple credentials. The first time this service
+ is used, the value pointed to by this argument should be
+ zero. Thereafter, this value will be updated to allow
+ retrieval to continue where it left off. This value should,
+ therefore, not be changed until all credentials of a given
+ query operation have been retrieved.
+
+ RetrieveAllCredentials - A boolean value indicating whether all
+ credentials for the specified logon session should be
+ retrieved (TRUE), or only those matching the specified
+ PrimaryKeyValue (FALSE).
+
+ PrimaryKeyValue - This parameter serves two purposes. If the
+ RetrieveAllCredentials argument is FALSE, then this string
+ contains the value to use as a primary key lookup value. In
+ this case, only credentials whose primary key matches this
+ one (and belonging to the correct logon session) will be
+ retrieved. If, however, the RetrieveAllCredentials argument
+ is FALSE, then the value of this string are ignored. In this
+ case, the primary key value of each retrieved credential will
+ be returned in this string.
+
+ PrimaryKeyLength - If the RetrieveAllCredentials argument value
+ is FALSE, then this argument receives the length needed to
+ store the PrimaryKeyValue. If this value is larger than the
+ length of the PrimaryKeyValue string, then
+ STATUS_BUFFER_OVERFLOW is returned and no data is retrieved.
+
+ Credentials - Points to a string whose buffer is to be set to
+ contain the retrieved credential.
+
+Return Status:
+
+ STATUS_MORE_ENTRIES - Credentials were successfully retrieved,
+ and there are more available.
+
+ STATUS_SUCCESS - Credentials were successfully retrieved and
+ there are no more available.
+
+ STATUS_UNSUCCESSFUL - No more credentials are available. If
+ returned on the first call, then there are no credentials
+ matching the selection criteria.
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
+ not be found.
+
+ STATUS_BUFFER_OVERFLOW - Indicates the string provided to receive
+ the PrimaryKeyValue was not large enough to hold the data.
+ In this case, no data was retrieved. However, the length value
+ is returned so that appropriately sized buffer can be passed in
+ a successive call.
+
+
+--*/
+
+{
+ //
+ // NOTE: The QueryContext value is an index of the last retrieved
+ // credential matching the selection criteria. To continue
+ // a search for successive credentials, skip QueryContext
+ // number of entries first.
+ //
+ // This has the problem of changes between calls screwing
+ // up the result of successive calls. That's tough.
+ //
+
+
+ NTSTATUS Status;
+ PLSAP_LOGON_SESSION LogonSession;
+ PLSAP_PACKAGE_CREDENTIALS Package;
+ PLSAP_CREDENTIALS NextCredentials;
+ ULONG i;
+ BOOLEAN SelectionMatch;
+
+
+ LsapAuLock();
+
+ //
+ // Get a pointer to the logon session
+ //
+
+ LogonSession = LsapGetLogonSession( LogonId, FALSE );
+
+ if ( LogonSession == NULL ) {
+ LsapAuUnlock();
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+
+ //
+ // Now get a pointer to the Package's credentials
+ //
+
+ Package = LsapGetPackageCredentials(
+ LogonSession,
+ AuthenticationPackage,
+ FALSE
+ );
+
+ if ( Package == NULL ) {
+ LsapAuUnlock();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // skip the credentials already evaluated in previous calls...
+ //
+
+ i = (*QueryContext);
+ NextCredentials = Package->Credentials;
+ while ( i > 0 ) {
+
+ //
+ // See if we have reached the end of the list
+ //
+
+ if (NextCredentials == NULL) {
+ LsapAuUnlock();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Nope, skip the next one...
+ //
+
+ NextCredentials = NextCredentials->NextCredentials;
+ i -= 1;
+
+ }
+
+
+ //
+ // Start evaluating each credential for a criteria match.
+ //
+
+ SelectionMatch = FALSE;
+ while ( NextCredentials != NULL && !SelectionMatch ) {
+
+ (*QueryContext) += 1;
+
+ if (RetrieveAllCredentials) {
+
+ SelectionMatch = TRUE;
+ Status = LsapReturnCredential(
+ NextCredentials,
+ Credentials,
+ TRUE,
+ PrimaryKeyValue,
+ PrimaryKeyLength
+ );
+ }
+
+ //
+ // Only retrieving credentials that match the specified primary
+ // key.
+ //
+
+ if ( RtlEqualString( &NextCredentials->PrimaryKey, PrimaryKeyValue, FALSE) ) {
+
+ SelectionMatch = TRUE;
+ Status = LsapReturnCredential(
+ NextCredentials,
+ Credentials,
+ FALSE,
+ NULL,
+ NULL
+ );
+
+ }
+
+ NextCredentials = NextCredentials->NextCredentials;
+
+ }
+
+ LsapAuUnlock();
+
+ //
+ // Figure out what return value to send.
+ //
+
+ if (SelectionMatch) {
+
+ if ( Status == STATUS_BUFFER_OVERFLOW ) {
+ (*QueryContext) -= 1;
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+
+ if ( Status == STATUS_SUCCESS) {
+ if ( NextCredentials == NULL ) {
+ return STATUS_SUCCESS;
+ } else {
+ return STATUS_MORE_ENTRIES;
+ }
+ }
+
+
+ } else {
+
+ //
+ // didn't find a credential matching the selection criteria.
+ //
+
+ return STATUS_UNSUCCESSFUL;
+
+ }
+
+}
+
+
+NTSTATUS
+LsapReturnCredential(
+ IN PLSAP_CREDENTIALS SourceCredentials,
+ IN PSTRING TargetCredentials,
+ IN BOOLEAN ReturnPrimaryKey,
+ IN PSTRING PrimaryKeyValue OPTIONAL,
+ OUT PULONG PrimaryKeyLength OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns a copy of the credentials in the specified
+ credential record. It also, optionally, returns a copy of the
+ primary key value.
+
+Arguments:
+
+ SourceCredentials - Points to a credential record whose credential
+ string and, optionally, primary key are to be copied.
+
+ TargetCredentials - Points to a string whose buffer is to be set to
+ contain a copy of the credential. This copy will be allocated
+ using LsapAllocateLsaHeap().
+
+ ReturnPrimaryKey - A boolean indicating whether or not to return
+ a copy of the primary key. TRUE indicates a copy should be
+ returned. FALSE indicates a copy should not be returned.
+
+ PrimaryKeyValue - Points to a string whose buffer is to be set to
+ contain a copy of the primary key. This copy will be allocated
+ using LsapAllocateLsaHeap(). This parameter is ignored if the
+ ReturnPrimaryKey argument value is FALSE.
+
+
+ PrimaryKeyLength - Points to a value which will receive the
+ length of the primary key value. If this value is larger than the
+ length of the PrimaryKeyValue string, then STATUS_BUFFER_OVERFLOW
+ is returned and no data is retrieved.
+
+
+
+Return Status:
+
+ STATUS_SUCCESS - Credentials were successfully returned.
+
+ STATUS_BUFFER_OVERFLOW - Indicates the string provided to receive
+ the PrimaryKeyValue was not large enough to hold the data.
+ In this case, no data was retrieved. However, the length value
+ is returned so that appropriately sized buffer can be passed in
+ a successive call.
+
+--*/
+
+{
+ ULONG Length;
+
+ //
+ // First try to return the primary key value, since we can encounter
+ // a buffer overflow situation in doing so that would prevent us from
+ // returning a copy of the credential string.
+ //
+
+ if (ReturnPrimaryKey) {
+ (*PrimaryKeyLength) = SourceCredentials->PrimaryKey.Length + 1;
+ if ( (*PrimaryKeyLength) > PrimaryKeyValue->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ //
+ // It fits
+ //
+
+ RtlCopyString( PrimaryKeyValue, &SourceCredentials->PrimaryKey );
+ }
+
+ //
+ // Now allocate and copy the credential string copy.
+ //
+
+ TargetCredentials->MaximumLength = SourceCredentials->Credentials.Length
+ + (USHORT)1;
+ Length = (ULONG)TargetCredentials->MaximumLength;
+ TargetCredentials->Buffer = (PCHAR)LsapAllocateLsaHeap( Length );
+ RtlCopyString( TargetCredentials, &SourceCredentials->Credentials );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+NTSTATUS
+LsapDeleteCredential(
+ IN PLUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN PSTRING PrimaryKeyValue
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used to delete an existing credential. This service
+ deletes the first credential it finds with a matching logon session,
+ authentication package ID, and primary key value. If thee are
+ multiple credentials that match this criteria, only one of them is
+ deleted.
+
+ This status acquires the AuLock.
+
+Arguments:
+
+ LogonId - The session ID of logon session whose credentials are to be
+ deleted.
+
+ AuthenticationPackage - The authentication package ID of the
+ calling authentication package. This was received in the
+ InitializePackage() call during DLL initialization.
+
+ PrimaryKeyValue - Points to string containing the primary key value
+ of the credential to be deleted.
+
+
+Return Status:
+
+ STATUS_SUCCESS - The credentials were successfully deleted.
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
+ not be found.
+
+ STATUS_UNSUCCESSFUL - No such credential could be found.
+
+--*/
+
+{
+
+
+ PLSAP_LOGON_SESSION LogonSession;
+ PLSAP_PACKAGE_CREDENTIALS Package;
+ PLSAP_CREDENTIALS *NextCredentials, GoodByeCredentials;
+
+
+
+
+ LsapAuLock();
+
+ //
+ // Get a pointer to the logon session
+ //
+
+ LogonSession = LsapGetLogonSession( LogonId, FALSE );
+
+ if ( LogonSession == NULL ) {
+ LsapAuUnlock();
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+
+ //
+ // Now get a pointer to the Package's credentials
+ //
+
+ Package = LsapGetPackageCredentials(
+ LogonSession,
+ AuthenticationPackage,
+ FALSE
+ );
+
+ if ( Package == NULL ) {
+ LsapAuUnlock();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+
+
+ //
+ // Start evaluating each credential for a primary key value match.
+ //
+
+ NextCredentials = &Package->Credentials;
+ while ( (*NextCredentials) != NULL ) {
+
+
+ if ( RtlEqualString(
+ &(*NextCredentials)->PrimaryKey,
+ PrimaryKeyValue,
+ FALSE)
+ ) {
+
+ //
+ // remove it from the list
+ //
+
+ GoodByeCredentials = (*NextCredentials);
+ (*NextCredentials) = GoodByeCredentials->NextCredentials;
+
+ //
+ // Free the contents of the credential record.
+ //
+
+ LsapFreeLsaHeap( GoodByeCredentials->PrimaryKey.Buffer );
+ LsapFreeLsaHeap( GoodByeCredentials->Credentials.Buffer );
+
+ //
+ // Free the credential record itself.
+ //
+
+ LsapFreeLsaHeap( GoodByeCredentials );
+
+ LsapAuUnlock();
+
+ return STATUS_SUCCESS;
+
+
+ }
+
+ NextCredentials = &(*NextCredentials)->NextCredentials;
+
+ }
+
+ LsapAuUnlock();
+
+ //
+ // Nothing matched
+ //
+
+ return STATUS_UNSUCCESSFUL;
+
+}
+
+
+PLSAP_PACKAGE_CREDENTIALS
+LsapGetPackageCredentials(
+ IN PLSAP_LOGON_SESSION LogonSession,
+ IN ULONG PackageId,
+ IN BOOLEAN CreateIfNecessary
+ )
+
+
+/*++
+
+Routine Description:
+
+ This service returns a pointer to a specified package's credential
+ record. If no such record exists, one will optionally be created.
+
+ It is assumed that either the LogonSession record is not currently
+ in the logon session record list, or, if it is, that the AuLock
+ is currently held.
+
+Arguments:
+
+ LogonSession - Pointer to a logon session record within which to
+ work.
+
+ PackageId - The authentication package ID to look for.
+
+ CreateIfNecessary - A boolean indicating whether or not the package
+ record is to be created if one does not already exist. TRUE
+ indicates the package is to be created if necessary, FALSE indicates
+ the record should not be created.
+
+
+Return Status:
+
+ non-NULL - A pointer to the specified package record.
+
+ NULL - The specified package record does not exist (and one was not
+ created automatically).
+
+--*/
+
+{
+
+ PLSAP_PACKAGE_CREDENTIALS *NextPackage, TargetPackage;
+
+
+ //
+ // See if the session exists
+ //
+
+ NextPackage = &LogonSession->Packages;
+
+ while ( (*NextPackage) != NULL) {
+ if ( (*NextPackage)->PackageId == PackageId ) {
+
+ //
+ // Found it
+ //
+
+ TargetPackage = (*NextPackage);
+
+
+ return TargetPackage;
+
+ }
+
+ //
+ // Move on to next package.
+ //
+
+ NextPackage = &(*NextPackage)->NextPackage;
+
+ }
+
+ //
+ // No such package exists yet.
+ // Create one if necessary.
+
+ if ( !CreateIfNecessary ) {
+ return NULL;
+ }
+
+ TargetPackage = LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_PACKAGE_CREDENTIALS) );
+ TargetPackage->PackageId = PackageId;
+ TargetPackage->Credentials = NULL;
+ TargetPackage->NextPackage = LogonSession->Packages;
+ LogonSession->Packages = TargetPackage;
+
+ return TargetPackage;
+
+}
+
+VOID
+LsapFreePackageCredentialList(
+ IN PLSAP_PACKAGE_CREDENTIALS PackageCredentialList
+ )
+
+/*++
+
+Routine Description:
+
+ This service frees a list of packge credential records. This service
+ is not expected to be exposed to authentication packages.
+
+ This service expects not to have to acquire the AuLock. This may be
+ because it is already held, or because the credentials being freed
+ are no longer accessible via the global variables.
+
+Arguments:
+
+ PackageCredentialList - Is a pointer to a list of LSA_PACKAGE_CREDENTIALS
+ data structures.
+
+
+Return Status:
+
+ None.
+
+--*/
+
+{
+
+ PLSAP_PACKAGE_CREDENTIALS NextPackage, GoodByePackage;
+
+
+
+ //
+ // Get rid of each PACKAGE_CREDENTIAL record.
+ //
+
+ NextPackage = PackageCredentialList;
+ while ( NextPackage != NULL ) {
+
+ //
+ // Save a pointer to the next package
+ //
+
+ GoodByePackage = NextPackage;
+ NextPackage = GoodByePackage->NextPackage;
+
+
+ LsapFreeCredentialList( GoodByePackage->Credentials );
+
+
+ //
+ // Free the package record itself.
+ //
+
+ LsapFreeLsaHeap( GoodByePackage );
+
+
+ }
+
+
+ return;
+
+}
+
+
+VOID
+LsapFreeCredentialList(
+ IN PLSAP_CREDENTIALS CredentialList
+ )
+
+/*++
+
+Routine Description:
+
+ This service frees a list of credential records. This service is not
+ expected to be exposed to authentication packages.
+
+ This service expects not to have to acquire the AuLock. This may be
+ because it is already held, or because the credentials being freed
+ are no longer accessible via the global variables.
+
+Arguments:
+
+ CredentialList - Is a pointer to a list of LSA_CREDENTIALS data
+ structures.
+
+
+Return Status:
+
+
+--*/
+
+{
+
+ PLSAP_CREDENTIALS NextCredentials, GoodByeCredentials;
+
+ //
+ // Get rid of each PACKAGE_CREDENTIAL record.
+ //
+
+ NextCredentials = CredentialList;
+ while ( NextCredentials != NULL ) {
+
+ //
+ // Save a pointer to the next credential
+ //
+
+ GoodByeCredentials = NextCredentials;
+ NextCredentials = GoodByeCredentials->NextCredentials;
+
+ //
+ // Free the contents of this credential record.
+ //
+
+ LsapFreeLsaHeap( GoodByeCredentials->PrimaryKey.Buffer );
+ LsapFreeLsaHeap( GoodByeCredentials->Credentials.Buffer );
+
+ //
+ // Free the credential record itself.
+ //
+
+ LsapFreeLsaHeap( GoodByeCredentials );
+
+ }
+
+ return;
+
+}
diff --git a/private/lsa/server/auctxt.c b/private/lsa/server/auctxt.c
new file mode 100644
index 000000000..0155c1536
--- /dev/null
+++ b/private/lsa/server/auctxt.c
@@ -0,0 +1,456 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ auctxt.c
+
+Abstract:
+
+ Logon process context management services.
+
+Author:
+
+ Jim Kelly (JimK) 7-May-1993
+
+Revision History:
+
+--*/
+
+#include "ausrvp.h"
+#include <windows.h>
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Global variables //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+//
+// This resource is used to gain exclusive access to the linked list
+// of client context blocks.
+//
+
+static
+RTL_RESOURCE
+ LsapAuClientContextLock;
+
+
+static
+LIST_ENTRY
+ LsapAuClientContextListHead;
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Exported Services //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapAuInitializeContextMgr(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes the global components of the client
+ context management services. This includes a initializing a
+ database lock and a list of client contexts.
+
+
+Arguments:
+
+ None.
+
+
+Return Value:
+
+ Only STATUS_SUCCESS is expected. But, if we encounter an error
+ initializing then the error we hit will be returned.
+
+--*/
+
+{
+
+ //
+ // Initialize the database lock
+ //
+
+ RtlInitializeResource(&LsapAuClientContextLock);
+
+
+ //
+ // Initialize the context list to be empty.
+ //
+
+ InitializeListHead( &LsapAuClientContextListHead );
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+VOID
+LsapAuAddClientContext(
+ PLSAP_LOGON_PROCESS Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds a new client context to the list of
+ valid logon process contexts.
+
+ After adding a new context, that context has been referenced
+ to allow the caller to continue using it. Therefore, the
+ caller is expected to dereference the context before completing
+ the LPC call.
+
+ This routine will initialize the Links and References fields
+ of the client context.
+
+ Details:
+
+ 1) Locks the context database.
+
+ 2) Set's the context's reference count to 2,
+ one for being on the context list, one because
+ the caller is using it. This means the caller
+ must call LsapAuDereferenceClientContext() after
+ adding the context.
+
+ 3) Adds the context to the context list.
+
+ 4) Unlocks the context database.
+
+
+
+Arguments:
+
+ Context - Points to the client's request whose context
+ is to be added.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ BOOLEAN
+ Success;
+
+ //
+ // Acquire exclusive access to the context list
+ //
+
+ Success = RtlAcquireResourceExclusive( &LsapAuClientContextLock, TRUE );
+ ASSERT(Success);
+
+
+ //
+ // The reference count is set to 2. 1 to indicate it is on the
+ // valid context list, and one for the caller.
+ //
+
+ Context->References = 2;
+
+ //
+ // Add it to the list of contexts.
+ //
+
+ InsertHeadList( &LsapAuClientContextListHead, &Context->Links );
+#ifdef LSAP_AU_TRACK_CONTEXT
+ DbgPrint("lsa (au): Adding client context 0x%lx\n", Context);
+#endif //LSAP_AU_TRACK_CONTEXT
+
+
+
+ //
+ // Free the lock
+ //
+
+ RtlReleaseResource(&LsapAuClientContextLock);
+ return;
+
+}
+
+
+BOOLEAN
+LsapAuReferenceClientContext(
+ PLSAP_CLIENT_REQUEST ClientRequest,
+ BOOLEAN RemoveContext,
+ PBOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks to see if the client request is from a currently
+ active client, and references the context if it is valid.
+
+ The caller may optionally request that the client's context be
+ removed from the list of valid contexts - preventing future
+ requests from finding this context.
+
+ For a client's context to be valid, the request's context value
+ must be on our list of active logon processes.
+
+ NOTE: We can't require the caller's ClientId to match that of the
+ caller that registered. This is because the LM Redirector
+ calls from random processes, but attaches to the process it
+ did the register from before calling. LPC considers the call
+ to have come from the random process, not the attatched
+ process, and so the client IDs don't match.
+
+
+
+ Datails:
+
+ 1) Lock the context database and verifies that the
+ specified context is valid.
+
+ 2) Increment the context's reference count.
+
+ 3) If requested, remove the context from the
+ context list and decrement the reference count
+ (to indicate it is no longer on the list). This
+ prevents future lookups from finding the context.
+
+ 4) Unlock the context database.
+
+
+Arguments:
+
+ ClientRequest - Points to the client's request whose context
+ is to be referenced. This is not necessarily a complete
+ client request. However, the context pointer and the client
+ IDs are expected to be valid.
+
+ RemoveContext - This boolean value indicates whether the caller
+ wants the logon process's context to be removed from the list
+ of contexts. TRUE indicates the context is to be removed.
+ FALSE indicates the context is not to be removed.
+
+
+Return Value:
+
+ TRUE - the context was found and was referenced.
+
+ FALSE - the context was not found.
+
+--*/
+
+{
+ BOOLEAN
+ Success;
+
+ PLIST_ENTRY
+ Next;
+
+ PLSAP_LOGON_PROCESS
+ Context;
+
+
+ //
+ // Acquire exclusive access to the context list
+ //
+
+ Success = RtlAcquireResourceExclusive( &LsapAuClientContextLock, TRUE );
+ ASSERT(Success);
+
+ //
+ // Now walk the list of contexts looking for a match.
+ //
+
+ Next = LsapAuClientContextListHead.Flink;
+ while (Next != &LsapAuClientContextListHead) {
+
+ if ((PVOID)Next ==(PVOID)(ClientRequest->LogonProcessContext)) {
+
+ Context = (PLSAP_LOGON_PROCESS)Next;
+
+ //
+ // Found a match ... reference this context
+ // (if the context is being removed, we would increment
+ // and then decrement the reference, so don't bother doing
+ // either - since they cancel each other out).
+ //
+
+ if (!RemoveContext) {
+ Context->References += 1;
+ } else {
+
+ RemoveEntryList( Next );
+#ifdef LSAP_AU_TRACK_CONTEXT
+ DbgPrint("lsa (au): Removing client context 0x%lx\n", Context);
+#endif //LSAP_AU_TRACK_CONTEXT
+ }
+
+ RtlReleaseResource(&LsapAuClientContextLock);
+
+ *TrustedClient = Context->TrustedClient;
+
+ return(TRUE);
+
+ }
+
+
+ //
+ // Wasn't this one, move on to the next one.
+ //
+
+ Next = Next->Flink;
+ }
+
+
+ //
+ // No match found
+ //
+
+#ifdef LSAP_AU_TRACK_CONTEXT
+ DbgPrint("lsa\\server: (au) Call from unknown client.\n");
+ Next = (PLIST_ENTRY)ClientRequest->LogonProcessContext;
+ DbgPrint(" Context (0x%lx)\n", Next);
+ DbgPrint(" Context Entry (0x%lx)\n", Next);
+ DbgPrint(" Flink: 0x%lx\n", Next->Flink );
+ DbgPrint(" Blink: 0x%lx\n", Next->Blink );
+ DbgPrint(" Ref: %d\n", ((PLSAP_LOGON_PROCESS)Next)->References);
+ DbgPrint(" Proc: 0x%lx\n", ((PLSAP_LOGON_PROCESS)Next)->ClientProcess);
+ DbgPrint(" Comm: 0x%lx\n", ((PLSAP_LOGON_PROCESS)Next)->CommPort);
+
+ Next = LsapAuClientContextListHead.Flink;
+ DbgPrint(" Active context list head: (%lx, %lx)\n",
+ LsapAuClientContextListHead.Flink,
+ LsapAuClientContextListHead.Blink);
+
+ while (Next != &LsapAuClientContextListHead) {
+ DbgPrint(" Context Entry (0x%lx)\n", Next);
+ DbgPrint(" Flink: 0x%lx\n", Next->Flink );
+ DbgPrint(" Blink: 0x%lx\n", Next->Blink );
+ DbgPrint(" Ref: %d\n", ((PLSAP_LOGON_PROCESS)Next)->References);
+ DbgPrint(" Proc: 0x%lx\n", ((PLSAP_LOGON_PROCESS)Next)->ClientProcess);
+ DbgPrint(" Comm: 0x%lx\n", ((PLSAP_LOGON_PROCESS)Next)->CommPort);
+ Next = Next->Flink;
+ }
+#endif //LSAP_AU_TRACK_CONTEXT
+
+ ClientRequest->Request->ReturnedStatus = STATUS_INVALID_PARAMETER;
+ RtlReleaseResource(&LsapAuClientContextLock);
+ return(FALSE);
+
+}
+
+
+VOID
+LsapAuDereferenceClientContext(
+ PLSAP_LOGON_PROCESS Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decrements the specified context's reference count.
+ If the reference count drops to zero, then the context is run-down.
+
+ Details:
+
+ 1) Locks the context database.
+
+ 2) Decrements the context's reference count.
+
+ 3) If the reference count drops to zero, then
+ rundown the logon process (close open handles
+ and free the context block memory).
+
+ 4) Unlocks the context database.
+
+Arguments:
+
+ Context - Points to the context to be dereferenced.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOLEAN
+ Success;
+
+ NTSTATUS
+ IgnoreStatus;
+
+ //
+ // Acquire exclusive access to the context list
+ //
+
+ Success = RtlAcquireResourceExclusive( &LsapAuClientContextLock, TRUE );
+ ASSERT(Success);
+
+
+ //
+ // Decrement the reference count
+ //
+
+ ASSERT( Context->References >= 1 );
+ Context->References -= 1;
+
+ //
+ // If the count dropped to zero, then run-down the context
+ //
+
+ if (Context->References == 0) {
+
+#ifdef LSAP_AU_TRACK_CONTEXT
+ DbgPrint("lsa (au): Deleting client context 0x%lx\n", Context);
+#endif //LSAP_AU_TRACK_CONTEXT
+
+#if DBG
+ //
+ // For debug systems, walk the list of contexts looking for a match.
+ // If we find this context on the list, then ASSERT.
+ //
+
+ {
+ PLIST_ENTRY
+ Next;
+
+ Next = LsapAuClientContextListHead.Flink;
+ while (Next != &LsapAuClientContextListHead) {
+ ASSERT((PVOID)Next != (PVOID)Context);
+ Next = Next->Flink;
+ }
+ }
+#endif //DBG
+
+ IgnoreStatus = LsapAuRundownLogonProcess( Context );
+
+
+ }
+
+ RtlReleaseResource(&LsapAuClientContextLock);
+ return;
+
+}
+
diff --git a/private/lsa/server/aufilter.c b/private/lsa/server/aufilter.c
new file mode 100644
index 000000000..8d349a540
--- /dev/null
+++ b/private/lsa/server/aufilter.c
@@ -0,0 +1,2009 @@
+
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ aufilter.c
+
+Abstract:
+
+ This module contains the famous LSA logon Filter/Augmentor logic.
+
+Author:
+
+ Jim Kelly (JimK) 11-Mar-1992
+
+Revision History:
+
+--*/
+
+#include <rpc.h>
+#include "lsasrvp.h"
+#include "ausrvp.h"
+
+//#define LSAP_DONT_ASSIGN_DEFAULT_DACL
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Module local macros //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+#define LsapFreeSampUlongArray( A ) \
+{ \
+ if ((A)->Element != NULL) { \
+ MIDL_user_free((A)->Element); \
+ } \
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Module-wide global variables //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+//
+// Indicates whether we have already opened SAM handles and initialized
+// corresponding variables.
+//
+
+ULONG LsapAuSamOpened = FALSE;
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Module local routine definitions //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+VOID
+LsapAuSetLogonPrivilegeStates(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG PrivilegeCount,
+ IN PLUID_AND_ATTRIBUTES Privileges
+ );
+
+NTSTATUS
+LsapAuSetPassedIds(
+ IN LSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID *TokenInformation,
+ OUT PULONG FinalIdCount,
+ OUT PSID_AND_ATTRIBUTES FinalIds,
+ OUT PULONG IdProperties
+ );
+
+
+NTSTATUS
+LsapSetDefaultDacl(
+ IN LSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID TokenInformation,
+ IN PULONG FinalIdCount,
+ IN PSID_AND_ATTRIBUTES FinalIds,
+ IN ULONG FinalOwnerIndex
+ );
+
+
+NTSTATUS
+LsapAuAddStandardIds(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN LSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN OUT PULONG FinalIdCount,
+ IN OUT PSID_AND_ATTRIBUTES FinalIds,
+ IN OUT PULONG IdProperties
+ );
+
+NTSTATUS
+LsapAuAddLocalAliases(
+ IN OUT PULONG FinalIdCount,
+ IN OUT PSID_AND_ATTRIBUTES FinalIds,
+ IN OUT PULONG IdProperties,
+ IN OUT PULONG FinalOwnerIndex
+ );
+
+NTSTATUS
+LsapGetAccountDomainInfo(
+ PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo
+ );
+
+NTSTATUS
+LsapAuVerifyLogonType(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG SystemAccess
+ );
+
+NTSTATUS
+LsapAuSetTokenInformation(
+ IN OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN OUT PVOID *TokenInformation,
+ IN ULONG FinalIdCount,
+ IN PSID_AND_ATTRIBUTES FinalIds,
+ IN PULONG IdProperties,
+ IN ULONG FinalOwnerIndex,
+ IN ULONG PrivilegeCount,
+ IN PLUID_AND_ATTRIBUTES Privileges
+ );
+
+NTSTATUS
+LsapAuCopySidAndAttributes(
+ PSID_AND_ATTRIBUTES Target,
+ PSID_AND_ATTRIBUTES Source,
+ PULONG SourceProperties
+ );
+
+NTSTATUS
+LsapAuCopySid(
+ PSID *Target,
+ PSID_AND_ATTRIBUTES Source,
+ PULONG SourceProperties
+ );
+
+BOOLEAN
+LsapIsSidLogonSid(
+ PSID Sid
+ );
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapAuUserLogonPolicyFilter(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID *TokenInformation,
+ OUT PQUOTA_LIMITS QuotaLimits,
+ OUT PPRIVILEGE_SET *PrivilegesAssigned
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs per-logon filtering and augmentation to
+ implement local system security policies. These policies include
+ assignment of local aliases, privileges, and quotas.
+
+ The basic logic flow of the filter augmentor is:
+
+
+ 1) Receive a set of user and group IDs that have already
+ been assigned as a result of authentication. Presumably
+ these IDs have been provided by the authenticating
+ security authority.
+
+
+ 2) Based upon the LogonType, add a set of standard IDs to the
+ list. This will include WORLD and an ID representing the
+ logon type (e.g., INTERACTIVE, NETWORK, SERVICE).
+
+
+ 3) Call SAM to retrieve additional ALIAS IDs assigned by the
+ local ACCOUNTS domain.
+
+
+ 4) Call SAM to retrieve additional ALIAS IDs assigned by the
+ local BUILTIN domain.
+
+
+ 5) Retrieve any privileges and or quotas assigned to the resultant
+ set of IDs. This also informs us whether or not the specific
+ type of logon is to be allowed. Enable privs for network logons.
+
+
+ 6) If a default DACL has not already been established, assign
+ one.
+
+
+ 7) Shuffle all high-use-rate IDs to preceed those that aren't
+ high-use-rate to obtain maximum performance.
+
+
+
+Arguments:
+
+ LogonType - Specifies the type of logon being requested (e.g.,
+ Interactive, network, et cetera).
+
+ TokenInformationType - Indicates what format the provided set of
+ token information is in.
+
+ TokenInformation - Provides the set of user and group IDs. This
+ structure will be modified as necessary to incorporate local
+ security policy (e.g., SIDs added or removed, privileges added
+ or removed).
+
+ QuotaLimits - Quotas assigned to the user logging on.
+
+Return Value:
+
+ STATUS_SUCCESS - The service has completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - heap could not be allocated to house
+ the combination of the existing and new groups.
+
+ STATUS_INVALID_LOGON_TYPE - The value specified for LogonType is not
+ a valid value.
+
+ STATUS_LOGON_TYPE_NOT_GRANTED - Indicates the user has not been granted
+ the requested type of logon by local security policy. Logon should
+ be rejected.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG i;
+ ULONG FinalIdCount, FinalPrivilegeCount, FinalOwnerIndex;
+ SID_AND_ATTRIBUTES FinalIds[LSAP_CONTEXT_SID_LIMIT];
+ ULONG IdProperties[LSAP_CONTEXT_SID_LIMIT];
+ PLUID_AND_ATTRIBUTES FinalPrivileges = NULL;
+ LSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO AccountInfo;
+
+ //
+ // Validate the Logon Type.
+ //
+
+ if ( LogonType != Interactive &&
+ LogonType != Network &&
+ LogonType != Service &&
+ LogonType != Batch ) {
+
+ Status = STATUS_INVALID_LOGON_TYPE;
+ goto UserLogonPolicyFilterError;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Build up a list of IDs and privileges to return //
+ // This list is initialized to contain the set of IDs //
+ // passed in. //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ //
+ // Start out with the IDs passed in and no privileges
+ //
+
+ FinalIdCount = 0;
+
+ Status = LsapAuSetPassedIds(
+ (*TokenInformationType),
+ TokenInformation,
+ &FinalIdCount,
+ FinalIds,
+ IdProperties
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UserLogonPolicyFilterError;
+ }
+
+ FinalOwnerIndex = 0;
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Copy in standard IDs (world and logon type) //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ Status = LsapAuAddStandardIds(
+ LogonType,
+ (*TokenInformationType),
+ &FinalIdCount,
+ FinalIds,
+ IdProperties
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UserLogonPolicyFilterError;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Copy in aliases from the local domains (BUILT-IN and ACCOUNT) //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ Status = LsapAuAddLocalAliases(
+ &FinalIdCount,
+ FinalIds,
+ IdProperties,
+ &FinalOwnerIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UserLogonPolicyFilterError;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Retrieve Privileges And Quotas //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ //
+ // Get the union of all Privileges, Quotas and System Accesses assigned
+ // to the user's list of ids from the LSA Policy Database.
+ //
+
+ FinalPrivilegeCount = 0;
+
+ Status = LsapDbQueryAllInformationAccounts(
+ (LSAPR_HANDLE) LsapPolicyHandle,
+ FinalIdCount,
+ FinalIds,
+ &AccountInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UserLogonPolicyFilterError;
+ }
+
+ //
+ // Verify that we have the necessary System Access for our logon type.
+ // We omit this check if we are using the NULL session.
+ //
+
+
+ if (*TokenInformationType != LsaTokenInformationNull) {
+
+ Status = LsapAuVerifyLogonType( LogonType, AccountInfo.SystemAccess );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UserLogonPolicyFilterError;
+ }
+ }
+
+ //
+ // Convert the Privilege Set returned by the Query routine to a Luid
+ // and ATTRIBUTES array. Free the Privilege Set.
+ //
+
+ Status = LsapRtlPrivilegeSetToLuidAndAttributes(
+ AccountInfo.PrivilegeSet,
+ &FinalPrivilegeCount,
+ &FinalPrivileges
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UserLogonPolicyFilterError;
+ }
+
+ //
+ // Return these so they can be audited. Data
+ // will be freed in the caller.
+ //
+
+ *PrivilegesAssigned = AccountInfo.PrivilegeSet;
+ AccountInfo.PrivilegeSet = NULL;
+
+ //
+ // Enable or Disable privileges according to our logon type
+ // This is necessary until we get dynamic security tracking.
+ //
+
+ LsapAuSetLogonPrivilegeStates(
+ LogonType,
+ FinalPrivilegeCount,
+ FinalPrivileges
+ );
+
+ *QuotaLimits = AccountInfo.QuotaLimits;
+
+#ifndef LSAP_DONT_ASSIGN_DEFAULT_DACL
+
+ Status = LsapSetDefaultDacl( (*TokenInformationType),
+ (*TokenInformation),
+ &FinalIdCount,
+ &FinalIds[0],
+ FinalOwnerIndex
+ );
+ if (!NT_SUCCESS(Status)) {
+
+ goto UserLogonPolicyFilterError;
+ }
+
+#endif //LSAP_DONT_ASSIGN_DEFAULT_DACL
+
+ //
+ // Now update the TokenInformation structure.
+ // This causes all allocated IDs and privileges to be
+ // freed (even if unsuccessful).
+ //
+
+ Status = LsapAuSetTokenInformation(
+ TokenInformationType,
+ TokenInformation,
+ FinalIdCount,
+ FinalIds,
+ IdProperties,
+ FinalOwnerIndex,
+ FinalPrivilegeCount,
+ FinalPrivileges
+ );
+
+
+UserLogonPolicyFilterFinish:
+
+ return(Status);
+
+UserLogonPolicyFilterError:
+
+ //
+ // Clean up any memory allocated for Sid properties.
+ //
+
+ for ( i=0; i<FinalIdCount; i++) {
+
+ if ((IdProperties[i] & LSAP_AU_SID_PROP_ALLOCATED) != 0) {
+
+ LsapFreeLsaHeap( FinalIds[i].Sid );
+ }
+ }
+
+ //
+ // If necessary, clean up Privileges buffer
+ //
+
+ if (FinalPrivileges != NULL) {
+
+ MIDL_user_free( FinalPrivileges );
+ FinalPrivileges = NULL;
+ }
+
+ goto UserLogonPolicyFilterFinish;
+}
+
+
+NTSTATUS
+LsapAuVerifyLogonType(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that a User has the system access granted necessary
+ for the speicifed logon type.
+
+Arguments
+
+ LogonType - Specifies the type of logon being requested (e.g.,
+ Interactive, network, et cetera).
+
+ SystemAccess - Specifies the System Access granted to the User.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The user has the necessary system access.
+
+ STATUS_LOGON_TYPE_NOT_GRANTED - Indicates the specified type of logon
+ has not been granted to any of the IDs in the passed set.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Determine if the specified Logon Type is granted by any of the
+ // groups or aliases specified.
+ //
+
+ switch (LogonType) {
+
+ case Interactive:
+
+ if (!(SystemAccess & SECURITY_ACCESS_INTERACTIVE_LOGON)) {
+
+ Status = STATUS_LOGON_TYPE_NOT_GRANTED;
+ }
+
+ break;
+
+ case Network:
+
+ if (!(SystemAccess & SECURITY_ACCESS_NETWORK_LOGON)) {
+
+ Status = STATUS_LOGON_TYPE_NOT_GRANTED;
+ }
+
+ break;
+
+ case Batch:
+
+ if (!(SystemAccess & SECURITY_ACCESS_BATCH_LOGON)) {
+
+ Status = STATUS_LOGON_TYPE_NOT_GRANTED;
+ }
+
+ break;
+
+ case Service:
+
+ if (!(SystemAccess & SECURITY_ACCESS_SERVICE_LOGON)) {
+
+ Status = STATUS_LOGON_TYPE_NOT_GRANTED;
+ }
+
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapAuSetPassedIds(
+ IN LSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID *TokenInformation,
+ OUT PULONG FinalIdCount,
+ OUT PSID_AND_ATTRIBUTES FinalIds,
+ OUT PULONG IdProperties
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes the FinalIds array.
+
+
+
+Arguments:
+
+
+ TokenInformationType - Indicates what format the provided set of
+ token information is in.
+
+ TokenInformation - Provides the initial set of user and group IDs.
+
+ FinalIdCount - Will be set to contain the number of IDs passed.
+
+ FinalIds - will contain the set of IDs passed in.
+
+ IdProperties - Will be set to indicate none of the initial
+ IDs were locally allocated. It will also identify the
+ first two ids (if there are two ids) to be HIGH_RATE.
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_TOO_MANY_CONTEXT_IDS - There are too many IDs in the context.
+
+
+--*/
+
+{
+
+ ULONG i, j, InitialIdCount;
+ PTOKEN_USER User;
+ PTOKEN_GROUPS Groups;
+ PTOKEN_PRIMARY_GROUP PrimaryGroup;
+ PSID PrimaryGroupSid;
+ PULONG PrimaryGroupAttributes;
+
+
+
+
+
+
+ //
+ // Get the passed ids
+ //
+
+ ASSERT( (TokenInformationType == LsaTokenInformationNull ) ||
+ (TokenInformationType == LsaTokenInformationV1) );
+
+ if (TokenInformationType == LsaTokenInformationNull) {
+ User = NULL;
+ Groups = ((PLSA_TOKEN_INFORMATION_NULL)(*TokenInformation))->Groups;
+ PrimaryGroup = NULL;
+ } else {
+ User = &((PLSA_TOKEN_INFORMATION_V1)(*TokenInformation))->User;
+ Groups = ((PLSA_TOKEN_INFORMATION_V1)(*TokenInformation))->Groups;
+ PrimaryGroup = &((PLSA_TOKEN_INFORMATION_V1)(*TokenInformation))->PrimaryGroup;
+ }
+
+
+ if (Groups != NULL) {
+ InitialIdCount = Groups->GroupCount;
+ } else {
+ InitialIdCount = 0;
+ }
+ if (User != NULL) {
+ InitialIdCount ++;
+ }
+ if (InitialIdCount > LSAP_CONTEXT_SID_LIMIT) {
+ return(STATUS_TOO_MANY_CONTEXT_IDS);
+ }
+
+
+ j = 0;
+ if (User != NULL) {
+
+ //
+ // TokenInformation included a user ID.
+ //
+
+ FinalIds[j] = User->User;
+ IdProperties[j] = LSAP_AU_SID_PROP_COPY;
+ j++;
+
+ }
+
+ if (PrimaryGroup != NULL) {
+ //
+ // TokenInformation included a primary group ID.
+ //
+
+ FinalIds[j].Sid = PrimaryGroup->PrimaryGroup;
+ FinalIds[j].Attributes = 0;
+
+ //
+ // Store a pointer to the attributes and the sid so we can later
+ // fill in the attributes from the rest of the group memebership.
+ //
+
+ PrimaryGroupAttributes = &FinalIds[j].Attributes;
+ PrimaryGroupSid = PrimaryGroup->PrimaryGroup;
+ IdProperties[j] = LSAP_AU_SID_PROP_COPY;
+ j++;
+ }
+
+ if (Groups != NULL) {
+ for (i=0; i < Groups->GroupCount; i++) {
+
+ //
+ // If this sid is the primary group, it is already in the list
+ // of final IDs but we need to add the attribute
+ //
+
+ if (RtlEqualSid(
+ PrimaryGroupSid,
+ Groups->Groups[i].Sid
+ )) {
+ *PrimaryGroupAttributes = Groups->Groups[i].Attributes;
+ } else {
+
+ FinalIds[j] = Groups->Groups[i];
+ IdProperties[j] = LSAP_AU_SID_PROP_COPY;
+
+ //
+ // if this SID is a logon SID, then set the SE_GROUP_LOGON_ID
+ // attribute
+ //
+
+ if (LsapIsSidLogonSid(FinalIds[j].Sid) == TRUE) {
+ FinalIds[j].Attributes |= SE_GROUP_LOGON_ID;
+ }
+ j++;
+
+ }
+
+
+ }
+ }
+
+
+ (*FinalIdCount) = InitialIdCount;
+
+
+ //
+ // We expect the user and primary group to be high hit rate IDs
+ //
+
+ if (InitialIdCount >= 2) {
+ IdProperties[0] |= (LSAP_AU_SID_PROP_HIGH_RATE);
+ IdProperties[1] |= (LSAP_AU_SID_PROP_HIGH_RATE);
+ }
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+
+NTSTATUS
+LsapSetDefaultDacl(
+ IN LSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID TokenInformation,
+ IN PULONG FinalIdCount,
+ IN PSID_AND_ATTRIBUTES FinalIds,
+ IN ULONG FinalOwnerIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine produces a default DACL if the existing TokenInformation
+ does not already have one. NULL logon types don't have default DACLs
+ and so this routine simply returns success for those logon types.
+
+
+ The default DACL will be:
+
+ SYSTEM: ALL Access
+ Owner: ALL Access
+
+
+ !! IMPORTANT !! IMPORTANT !! IMPORTANT !! IMPORTANT !!
+
+ NOTE: The FinalOwnerIndex should not be changed after
+ calling this routine.
+
+ !! IMPORTANT !! IMPORTANT !! IMPORTANT !! IMPORTANT !!
+
+
+Arguments:
+
+
+ TokenInformationType - Indicates what format the provided set of
+ token information is in.
+
+ TokenInformation - Points to token information which has the current
+ default DACL.
+
+ FinalIdCount - contains the number of IDs passed.
+
+ FinalIds - contains the set of user and group SIDs.
+
+ FinalOwnerIndex - Indicates which of the SIDs is the owner SID.
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_NO_MEMORY - Indicates there was not enough heap memory available
+ to allocate the default DACL.
+
+
+
+
+--*/
+
+{
+ NTSTATUS
+ Status;
+
+ PACL
+ Acl;
+
+ ULONG
+ Length;
+
+ SID_IDENTIFIER_AUTHORITY
+ NtAuthority = SECURITY_NT_AUTHORITY;
+
+ PLSA_TOKEN_INFORMATION_V1
+ CastTokenInformation;
+
+
+ //
+ // NULL token information?? (has no default dacl)
+ //
+
+ if (TokenInformationType == LsaTokenInformationNull) {
+ return(STATUS_SUCCESS);
+ }
+ ASSERT(TokenInformationType == LsaTokenInformationV1);
+
+
+ CastTokenInformation = (PLSA_TOKEN_INFORMATION_V1)TokenInformation;
+
+
+ //
+ // Already have a default DACL?
+ //
+
+ Acl = CastTokenInformation->DefaultDacl.DefaultDacl;
+ if (Acl != NULL) {
+ return(STATUS_SUCCESS);
+ }
+
+
+ //
+ // allocate and build default DACL...
+ //
+
+
+ Length = (ULONG)sizeof(ACL) +
+ (3*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
+ RtlLengthSid( FinalIds[FinalOwnerIndex].Sid ) +
+ RtlLengthSid( LsapLocalSystemSid );
+
+ Acl = (PACL)RtlAllocateHeap( RtlProcessHeap(), 0, Length);
+
+ if (Acl == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+
+ Status = RtlCreateAcl( Acl, Length, ACL_REVISION2);
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // OWNER access - put this one first for performance sake
+ //
+
+ Status = RtlAddAccessAllowedAce (
+ Acl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ FinalIds[FinalOwnerIndex].Sid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // SYSTEM access
+ //
+
+ Status = RtlAddAccessAllowedAce (
+ Acl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ LsapLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+
+ CastTokenInformation->DefaultDacl.DefaultDacl = Acl;
+
+ return(STATUS_SUCCESS);
+}
+
+
+
+NTSTATUS
+LsapAuAddStandardIds(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN LSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN OUT PULONG FinalIdCount,
+ IN OUT PSID_AND_ATTRIBUTES FinalIds,
+ IN OUT PULONG IdProperties
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds standard IDs to the FinalIds array.
+
+ This causes the WORLD id to be added and an ID representing
+ logon type to be added.
+
+ For anonymous logons, it will also add the ANONYMOUS id.
+
+
+
+
+
+Arguments:
+
+
+ LogonType - Specifies the type of logon being requested (e.g.,
+ Interactive, network, et cetera).
+
+ TokenInformationType - The token information type returned by
+ the authentication package. The set of IDs added is dependent
+ upon the type of logon.
+
+ FinalIdCount - Will be incremented to reflect newly added IDs.
+
+ FinalIds - will have new IDs added to it.
+
+ IdProperties - Will be set to indicate that these IDs must be
+ copied and that WORLD is a high-hit-rate id.
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_TOO_MANY_CONTEXT_IDS - There are too many IDs in the context.
+
+
+--*/
+
+{
+
+ ULONG i;
+
+ i = (*FinalIdCount);
+
+
+
+ //
+ // If this is a null logon, then add in the ANONYMOUS SID.
+ // (and make it the owner). We want this to be the first
+ // SID so that it is the default owner.
+ //
+
+ if (TokenInformationType == LsaTokenInformationNull) {
+ if ( i + 1 > LSAP_CONTEXT_SID_LIMIT) {
+ return(STATUS_TOO_MANY_CONTEXT_IDS);
+ }
+
+ FinalIds[i].Sid = LsapAnonymousSid; //Use the global SID
+ FinalIds[i].Attributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ IdProperties[i] = (LSAP_AU_SID_PROP_COPY);
+ i++;
+ }
+
+ //
+ // Add WORLD and something for logon type
+ //
+
+ if ( i + 2 > LSAP_CONTEXT_SID_LIMIT) {
+ return(STATUS_TOO_MANY_CONTEXT_IDS);
+ }
+
+ //
+ // WORLD
+ //
+
+ FinalIds[i].Sid = LsapWorldSid; //Use the global SID
+ FinalIds[i].Attributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ IdProperties[i] = (LSAP_AU_SID_PROP_COPY | LSAP_AU_SID_PROP_HIGH_RATE);
+ i++;
+
+
+
+
+ //
+ // Logon type SID
+ //
+
+ switch ( LogonType ) {
+ case Interactive:
+ FinalIds[i].Sid = LsapInteractiveSid;
+ break;
+ case Network:
+ FinalIds[i].Sid = LsapNetworkSid;
+ break;
+ case Batch:
+ FinalIds[i].Sid = LsapBatchSid;
+ break;
+ case Service:
+ FinalIds[i].Sid = LsapServiceSid;
+ break;
+ }
+
+ FinalIds[i].Attributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ IdProperties[i] = LSAP_AU_SID_PROP_COPY;
+ i++;
+
+
+ (*FinalIdCount) = i;
+ return(STATUS_SUCCESS);
+
+}
+
+
+
+NTSTATUS
+LsapAuAddLocalAliases(
+ IN OUT PULONG FinalIdCount,
+ IN OUT PSID_AND_ATTRIBUTES FinalIds,
+ IN OUT PULONG IdProperties,
+ IN OUT PULONG FinalOwnerIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds aliases assigned to the IDs in FinalIds.
+
+ This will look in both the BUILT-IN and ACCOUNT domains locally.
+
+
+ 1) Adds aliases assigned to the user via the local ACCOUNTS
+ domain.
+
+ 2) Adds aliases assigned to the user via the local BUILT-IN
+ domain.
+
+ 3) If the ADMINISTRATORS alias is assigned to the user, then it
+ is made the user's default owner.
+
+
+ NOTE: Aliases, by their nature, are expected to be high-use-rate
+ IDs.
+
+Arguments:
+
+
+ FinalIdCount - Will be incremented to reflect any newly added IDs.
+
+ FinalIds - will have any assigned alias IDs added to it.
+
+ IdProperties - Will be set to indicate that any aliases added were
+ allocated by this routine.
+
+ FinalOwnerIndex - Will be adjusted to assign ADMINISTRATORS as the
+ default owner, if the user is a member of that alias.
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_TOO_MANY_CONTEXT_IDS - There are too many IDs in the context.
+
+
+--*/
+
+{
+ NTSTATUS Status, SuccessExpected;
+ ULONG i,j;
+ BOOLEAN SetNewOwner = FALSE;
+ ULONG InitialIdCount, NewIdCount, NewOwner;
+ SAMPR_SID_INFORMATION SidArray[LSAP_CONTEXT_SID_LIMIT];
+ SAMPR_ULONG_ARRAY AccountMembership, BuiltinMembership;
+ ULONG MembershipCount;
+ PSID *MembershipSid;
+ SAMPR_PSID_ARRAY SamprSidArray;
+
+ //
+ // Make sure SAM has been opened. We'll get hadnles to both of the
+ // SAM Local Domains.
+ //
+
+ Status = LsapAuOpenSam();
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ InitialIdCount = (*FinalIdCount);
+
+ for ( i=0; i<InitialIdCount; i++) {
+
+ SidArray[i].SidPointer = (PRPC_SID)FinalIds[i].Sid;
+ }
+
+ SamprSidArray.Count = InitialIdCount;
+ SamprSidArray.Sids = &SidArray[0];
+
+ //
+ // For the given set of Sids, obtain their collective membership of
+ // Aliases in the Accounts domain
+ //
+
+ AccountMembership.Count = 0;
+ AccountMembership.Element = NULL;
+ Status = SamrGetAliasMembership( LsapAccountDomainHandle,
+ &SamprSidArray,
+ &AccountMembership
+ );
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ //
+ // For the given set of Sids, obtain their collective membership of
+ // Aliases in the Built-In domain
+ //
+
+ BuiltinMembership.Count = 0;
+ BuiltinMembership.Element = NULL;
+ Status = SamrGetAliasMembership( LsapBuiltinDomainHandle,
+ &SamprSidArray,
+ &BuiltinMembership
+ );
+ if (!NT_SUCCESS(Status)) {
+
+ LsapFreeSampUlongArray( &AccountMembership );
+ return(Status);
+ }
+
+ //
+ // Allocate memory to build the SIDs in.
+ //
+
+ MembershipCount = AccountMembership.Count;
+ MembershipCount += BuiltinMembership.Count;
+
+ if (MembershipCount != 0) {
+ MembershipSid = (PSID *)(LsapAllocateLsaHeap( MembershipCount * sizeof(PSID)));
+ Status = STATUS_INSUFFICIENT_RESOURCES; // Default status
+ } else {
+ MembershipSid = NULL;
+ Status = STATUS_SUCCESS;
+ }
+
+ if (MembershipSid == NULL) {
+ LsapFreeSampUlongArray( &AccountMembership );
+ LsapFreeSampUlongArray( &BuiltinMembership );
+ return(Status);
+ }
+
+
+ for ( i=0; i<MembershipCount; i++) {
+ MembershipSid[i] = NULL;
+ }
+
+ //
+ // Construct full Sids for all of the Account Domain Aliases returned.
+ //
+
+ for ( i=0; i<AccountMembership.Count; i++) {
+ MembershipSid[i] = LsapAllocateLsaHeap( LsapAccountDomainMemberSidLength );
+ if (MembershipSid[i] == NULL) {
+ goto au_local_alias_error;
+ }
+ SuccessExpected = RtlCopySid( LsapAccountDomainMemberSidLength,
+ MembershipSid[i],
+ LsapAccountDomainMemberSid
+ );
+ ASSERT(NT_SUCCESS(SuccessExpected));
+
+ (*RtlSubAuthoritySid( MembershipSid[i], LsapAccountDomainSubCount-1)) =
+ AccountMembership.Element[i];
+ }
+
+ //
+ // Construct full Sids for all of the Built-in Domain Aliases returned.
+ //
+
+ for ( j=0, i=AccountMembership.Count; i<MembershipCount; j++, i++) {
+
+ MembershipSid[i] = LsapAllocateLsaHeap( LsapBuiltinDomainMemberSidLength );
+ if (MembershipSid[i] == NULL) {
+ goto au_local_alias_error;
+ }
+ SuccessExpected = RtlCopySid( LsapBuiltinDomainMemberSidLength,
+ MembershipSid[i],
+ LsapBuiltinDomainMemberSid
+ );
+ ASSERT(NT_SUCCESS(SuccessExpected));
+
+ (*RtlSubAuthoritySid( MembershipSid[i], LsapBuiltinDomainSubCount-1)) =
+ BuiltinMembership.Element[j];
+
+ if (BuiltinMembership.Element[j] == DOMAIN_ALIAS_RID_ADMINS) {
+
+ //
+ // ADMINISTRATORS alias member - set it up as the default owner
+ //
+
+ SetNewOwner = TRUE;
+ NewOwner = i;
+ }
+ }
+
+ //
+ // Add the ids to the FinalIds array.
+ //
+
+ NewIdCount = InitialIdCount + MembershipCount;
+ if ( NewIdCount > LSAP_CONTEXT_SID_LIMIT) {
+ Status = STATUS_TOO_MANY_CONTEXT_IDS;
+ goto au_local_alias_error;
+ }
+
+
+ for ( j=0, i=InitialIdCount; i<NewIdCount; j++, i++) {
+
+ FinalIds[i].Sid = MembershipSid[j];
+ FinalIds[i].Attributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ IdProperties[i] = LSAP_AU_SID_PROP_ALLOCATED |
+ LSAP_AU_SID_PROP_HIGH_RATE;
+
+ }
+
+
+
+ (*FinalIdCount) = NewIdCount;
+
+
+ //
+ // If we need to, adjust the FinalOwnerIndex.
+ //
+
+ if ( SetNewOwner == TRUE) {
+ (*FinalOwnerIndex) = InitialIdCount + NewOwner;
+ FinalIds[(*FinalOwnerIndex)].Attributes |= (SE_GROUP_OWNER);
+ }
+
+
+ LsapFreeLsaHeap( MembershipSid );
+ LsapFreeSampUlongArray( &AccountMembership );
+ LsapFreeSampUlongArray( &BuiltinMembership );
+
+ return(STATUS_SUCCESS);
+
+
+au_local_alias_error:
+
+ //
+ // Appropriate return status must be set before coming here.
+ // Don't use this until the MembershipSid array is initialized.
+ //
+
+ LsapFreeSampUlongArray( &AccountMembership );
+ LsapFreeSampUlongArray( &BuiltinMembership );
+
+ for ( i=0; i<MembershipCount; i++) {
+ LsapFreeLsaHeap( MembershipSid[i] );
+ }
+ LsapFreeLsaHeap(MembershipSid);
+
+ return(Status);
+
+}
+
+
+
+NTSTATUS
+LsapAuSetTokenInformation(
+ IN OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN OUT PVOID *TokenInformation,
+ IN ULONG FinalIdCount,
+ IN PSID_AND_ATTRIBUTES FinalIds,
+ IN PULONG IdProperties,
+ IN ULONG FinalOwnerIndex,
+ IN ULONG PrivilegeCount,
+ IN PLUID_AND_ATTRIBUTES Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the information from the current TokenInformation,
+ the FinalIds array, and the Privileges and incorporates them into a
+ single TokenInformation structure. It may be necessary to free some
+ or all of the original TokenInformation. It may even be necessary to
+ produce a different TokenInformationType to accomplish this task.
+
+
+Arguments:
+
+
+ TokenInformationType - Indicates what format the provided set of
+ token information is in.
+
+ TokenInformation - The information in this structure will be superseeded
+ by the information in the FinalIDs parameter and the Privileges
+ parameter.
+
+ FinalIdCount - Indicates the number of IDs (user, group, and alias)
+ to be incorporated in the final TokenInformation.
+
+ FinalIds - Points to an array of SIDs and their corresponding
+ attributes to be incorporated into the final TokenInformation.
+
+ IdProperties - Points to an array of properties relating to the FinalIds.
+
+
+ FinalOwnerIndex - If zero, indicates that there is no explicit default
+ owner value. Otherwise, is the index of the default owner ID in
+ the FinalIds array.
+
+ PrivilegeCount - Indicates the number of privileges to be incorporated
+ into the final TokenInformation.
+
+ Privileges - Points to an array of privileges that are to be
+ incorporated into the TokenInformation. This array will be
+ used directly in the resultant TokenInformation.
+
+
+
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_NO_MEMORY - Indicates there was not enough heap memory available
+ to produce the final TokenInformation structure.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG Length, i;
+
+ PLSA_TOKEN_INFORMATION_V1 New, OldV1;
+ PLSA_TOKEN_INFORMATION_NULL OldNull;
+
+ ASSERT( FinalIdCount >= 2 );
+
+ ASSERT( *TokenInformationType == LsaTokenInformationV1 ||
+ *TokenInformationType == LsaTokenInformationNull);
+
+
+
+
+ //
+ // It is not worth trying to see if the original
+ // TokenInformation has everything that the final one should.
+ // Just go about building a new TokenInformation structure.
+ //
+
+ New = LsapAllocateLsaHeap( sizeof(LSA_TOKEN_INFORMATION_V1) );
+ if (New == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlZeroMemory( New, sizeof(LSA_TOKEN_INFORMATION_V1) );
+
+
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // //
+ // Set the ExpirationTime and DefaultAcl from the original //
+ // TokenInformation. //
+ // //
+ ////////////////////////////////////////////////////////////////////////
+
+ if ((*TokenInformationType) == LsaTokenInformationNull) {
+
+ OldNull = (PLSA_TOKEN_INFORMATION_NULL)(*TokenInformation);
+ New->ExpirationTime = OldNull->ExpirationTime;
+
+ } else {
+
+ OldV1 = (PLSA_TOKEN_INFORMATION_V1)(*TokenInformation);
+ New->ExpirationTime = OldV1->ExpirationTime;
+
+ //
+ // Move the DefaultDacl from the passed TokenInformation to the
+ // new TokenInformation. This is necessary to prevent the Dacl
+ // memory from being deallocate when the old TokenInformation is
+ // freed.
+ //
+
+ New->DefaultDacl = OldV1->DefaultDacl;
+ OldV1->DefaultDacl.DefaultDacl = NULL;
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // //
+ // User is the first ID in the list. //
+ // //
+ ////////////////////////////////////////////////////////////////////////
+
+ Status = LsapAuCopySidAndAttributes(
+ &New->User.User,
+ &FinalIds[0],
+ &IdProperties[0] );
+
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // //
+ // Set the groups. //
+ // //
+ ////////////////////////////////////////////////////////////////////////
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Don't count the UserID when building the TOKEN_GROUPS
+ //
+
+ Length = sizeof(TOKEN_GROUPS) +
+ (FinalIdCount-1-ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES);
+ New->Groups = LsapAllocateLsaHeap( Length );
+ if (New->Groups == NULL) {
+ Status = STATUS_NO_MEMORY;
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ // Take 2 passes through the groups. First copy
+ // the high-hit-rate IDs, then copy the rest of them.
+ //
+
+
+ ULONG i,k;
+
+ k=0; //Index into token groups structure;
+
+
+ New->Groups->GroupCount = 0;
+
+ //
+ // Start with the second entry - first is USER
+ //
+
+ for (i=1; (i<FinalIdCount && NT_SUCCESS(Status)); i++) {
+
+ if ((IdProperties[i] & LSAP_AU_SID_PROP_HIGH_RATE) != 0) {
+ Status = LsapAuCopySidAndAttributes(
+ &New->Groups->Groups[k],
+ &FinalIds[i],
+ &IdProperties[i]
+ );
+ New->Groups->GroupCount++;
+ k++;
+ }
+ }
+ for (i=1; (i<FinalIdCount && NT_SUCCESS(Status)); i++) {
+
+ if ((IdProperties[i] & LSAP_AU_SID_PROP_HIGH_RATE) == 0) {
+ Status = LsapAuCopySidAndAttributes(
+ &New->Groups->Groups[k],
+ &FinalIds[i],
+ &IdProperties[i]
+ );
+ New->Groups->GroupCount++;
+ k++;
+ }
+ }
+#ifdef DBG
+ if (NT_SUCCESS(Status)) {
+ ASSERT(k == New->Groups->GroupCount);
+ }
+#endif //DBG
+
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // //
+ // Primary group is the second ID in the list //
+ // //
+ ////////////////////////////////////////////////////////////////////////
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsapAuCopySid(
+ &New->PrimaryGroup.PrimaryGroup,
+ &FinalIds[1],
+ &IdProperties[1] );
+ }
+
+
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // //
+ // Set the Privileges, if any //
+ // //
+ ////////////////////////////////////////////////////////////////////////
+
+ if (NT_SUCCESS(Status) && (PrivilegeCount != 0)) {
+
+ Length = sizeof(TOKEN_PRIVILEGES) +
+ (PrivilegeCount-ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES);
+
+ New->Privileges = LsapAllocateLsaHeap( Length );
+ if (New->Privileges == NULL) {
+ Status = STATUS_NO_MEMORY;
+ } else {
+ New->Privileges->PrivilegeCount = PrivilegeCount;
+ for ( i=0; i<PrivilegeCount; i++) {
+ New->Privileges->Privileges[i] = Privileges[i];
+ }
+ }
+
+ MIDL_user_free( Privileges );
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // //
+ // Default owner, if explicit //
+ // //
+ ////////////////////////////////////////////////////////////////////////
+
+ if (NT_SUCCESS(Status) && FinalOwnerIndex != 0) {
+
+ Status = LsapAuCopySid(
+ &New->Owner.Owner,
+ &FinalIds[FinalOwnerIndex],
+ &IdProperties[FinalOwnerIndex] );
+ }
+
+
+
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // //
+ // Free the old TokenInformation and set the new //
+ // //
+ ////////////////////////////////////////////////////////////////////////
+
+
+ if (NT_SUCCESS(Status)) {
+
+ switch ( (*TokenInformationType) ) {
+ case LsaTokenInformationNull:
+ LsapFreeTokenInformationNull(
+ (PLSA_TOKEN_INFORMATION_NULL)(*TokenInformation));
+ break;
+
+
+ case LsaTokenInformationV1:
+ LsapFreeTokenInformationV1(
+ (PLSA_TOKEN_INFORMATION_V1)(*TokenInformation) );
+ break;
+ }
+
+
+ //
+ // Set the new TokenInformation
+ //
+
+ (*TokenInformationType) = LsaTokenInformationV1;
+ (*TokenInformation) = New;
+
+ } else {
+
+ //
+ // Something went wrong - free the new TokenInformationV1 structure
+ //
+
+ LsapFreeTokenInformationV1( New );
+ }
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsapAuCopySidAndAttributes(
+ PSID_AND_ATTRIBUTES Target,
+ PSID_AND_ATTRIBUTES Source,
+ PULONG SourceProperties
+ )
+
+/*++
+
+Routine Description:
+
+ Copy or reference a SID and its corresonding attributes.
+
+ The SID may be referenced if the SourceProperties indicate it
+ has been allocated. In this case, the SourceProperties must be
+ changed to indicate the SID is now a copy.
+
+
+Arguments:
+
+ Target - points to the SID_AND_ATTRIBUTES structure to receive
+ the copy of Source.
+
+ Source - points to the SID_AND_ATTRIBUTES structure to be copied.
+
+ SourceProperties - Contains LSAP_AU_SID_PROP_Xxx flags providing
+ information about the source. In some cases, the source may
+ be referenced instead of copied.
+
+Return Value:
+
+ STATUS_SUCCESS - The copy was successful.
+
+ STATUS_NO_MEMORY - memory could not be allocated to perform the copy.
+
+--*/
+
+{
+ ULONG Length;
+
+
+ if ((*SourceProperties) & LSAP_AU_SID_PROP_ALLOCATED) {
+
+ (*Target) = (*Source);
+ (*SourceProperties) &= ~LSAP_AU_SID_PROP_ALLOCATED;
+ (*SourceProperties) |= LSAP_AU_SID_PROP_COPY;
+
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // The SID needs to be copied ...
+ //
+
+ Length = RtlLengthSid( Source->Sid );
+ Target->Sid = LsapAllocateLsaHeap( Length );
+ if (Target->Sid == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ RtlMoveMemory( Target->Sid, Source->Sid, Length );
+ Target->Attributes = Source->Attributes;
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+NTSTATUS
+LsapAuCopySid(
+ PSID *Target,
+ PSID_AND_ATTRIBUTES Source,
+ PULONG SourceProperties
+ )
+
+/*++
+
+Routine Description:
+
+ Copy or reference a SID.
+
+ The SID may be referenced if the SourceProperties indicate it
+ has been allocated. In this case, the SourceProperties must be
+ changed to indicate the SID is now a copy.
+
+
+Arguments:
+
+ Target - Recieves a pointer to the SID copy.
+
+ Source - points to a SID_AND_ATTRIBUTES structure containing the SID
+ to be copied.
+
+ SourceProperties - Contains LSAP_AU_SID_PROP_Xxx flags providing
+ information about the source. In some cases, the source may
+ be referenced instead of copied.
+
+Return Value:
+
+ STATUS_SUCCESS - The copy was successful.
+
+ STATUS_NO_MEMORY - memory could not be allocated to perform the copy.
+
+--*/
+
+{
+ ULONG Length;
+
+
+ if ((*SourceProperties) & LSAP_AU_SID_PROP_ALLOCATED) {
+
+ (*Target) = Source->Sid;
+ (*SourceProperties) &= ~LSAP_AU_SID_PROP_ALLOCATED;
+ (*SourceProperties) |= LSAP_AU_SID_PROP_COPY;
+
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // The SID needs to be copied ...
+ //
+
+ Length = RtlLengthSid( Source->Sid );
+ (*Target) = LsapAllocateLsaHeap( Length );
+ if ((*Target == NULL)) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ RtlMoveMemory( (*Target), Source->Sid, Length );
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+
+NTSTATUS
+LsapAuOpenSam( VOID )
+
+/*++
+
+Routine Description:
+
+ This routine opens SAM for use during authentication. It
+ opens a handle to both the BUILTIN domain and the ACCOUNT domain.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+
+
+ if (LsapAuSamOpened == TRUE) {
+ return(STATUS_SUCCESS);
+ }
+
+ Status = LsapOpenSam();
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ //
+ // Set up the Built-In Domain Member Sid Information.
+ //
+
+ LsapBuiltinDomainSubCount = (*RtlSubAuthorityCountSid(LsapBuiltInDomainSid) + 1);
+ LsapBuiltinDomainMemberSidLength = RtlLengthRequiredSid( LsapBuiltinDomainSubCount );
+
+ //
+ // Get the member Sid information for the account domain
+ // and set the global variables related to this information.
+ //
+
+ Status = LsapGetAccountDomainInfo( &PolicyAccountDomainInfo );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ LsapAccountDomainSubCount =
+ (*(RtlSubAuthorityCountSid( PolicyAccountDomainInfo->DomainSid ))) +
+ (UCHAR)(1);
+ LsapAccountDomainMemberSidLength =
+ RtlLengthRequiredSid( (ULONG)LsapAccountDomainSubCount );
+
+ //
+ // Build typical SIDs for members of the BUILTIN and ACCOUNT domains.
+ // These are used to build SIDs when API return only RIDs.
+ // Don't bother setting the last RID to any particular value.
+ // It is always changed before use.
+ //
+
+ LsapAccountDomainMemberSid = LsapAllocateLsaHeap( LsapAccountDomainMemberSidLength );
+ if (LsapAccountDomainMemberSid != NULL) {
+ LsapBuiltinDomainMemberSid = LsapAllocateLsaHeap( LsapBuiltinDomainMemberSidLength );
+ if (LsapBuiltinDomainMemberSid == NULL) {
+ LsapFreeLsaHeap( LsapAccountDomainMemberSid );
+ LsaFreeMemory( PolicyAccountDomainInfo );
+ }
+ }
+
+ IgnoreStatus = RtlCopySid( LsapAccountDomainMemberSidLength,
+ LsapAccountDomainMemberSid,
+ PolicyAccountDomainInfo->DomainSid);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ (*RtlSubAuthorityCountSid(LsapAccountDomainMemberSid))++;
+
+ IgnoreStatus = RtlCopySid( LsapBuiltinDomainMemberSidLength,
+ LsapBuiltinDomainMemberSid,
+ LsapBuiltInDomainSid);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ (*RtlSubAuthorityCountSid(LsapBuiltinDomainMemberSid))++;
+
+
+ //
+ // Free the ACCOUNT domain information
+ //
+
+ LsaFreeMemory( PolicyAccountDomainInfo );
+
+ if (NT_SUCCESS(Status)) {
+ LsapAuSamOpened = TRUE;
+ }
+
+ return(Status);
+}
+
+
+
+
+BOOLEAN
+LsapIsSidLogonSid(
+ PSID Sid
+ )
+/*++
+
+Routine Description:
+
+ Test to see if the provided sid is a LOGON_ID.
+ Such sids start with S-1-5-5 (see ntseapi.h for more on logon sids).
+
+
+
+Arguments:
+
+ Sid - Pointer to SID to test. The SID is assumed to be a valid SID.
+
+
+Return Value:
+
+ TRUE - Sid is a logon sid.
+
+ FALSE - Sid is not a logon sid.
+
+--*/
+{
+ SID *ISid;
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+ ISid = Sid;
+
+
+ //
+ // if the identifier authority is SECURITY_NT_AUTHORITY and
+ // there are SECURITY_LOGON_IDS_RID_COUNT sub-authorities
+ // and the first sub-authority is SECURITY_LOGON_IDS_RID
+ // then this is a logon id.
+ //
+
+
+ if (ISid->SubAuthorityCount == SECURITY_LOGON_IDS_RID_COUNT) {
+ if (ISid->SubAuthority[0] == SECURITY_LOGON_IDS_RID) {
+ if (
+ (ISid->IdentifierAuthority.Value[0] == NtAuthority.Value[0]) &&
+ (ISid->IdentifierAuthority.Value[1] == NtAuthority.Value[1]) &&
+ (ISid->IdentifierAuthority.Value[2] == NtAuthority.Value[2]) &&
+ (ISid->IdentifierAuthority.Value[3] == NtAuthority.Value[3]) &&
+ (ISid->IdentifierAuthority.Value[4] == NtAuthority.Value[4]) &&
+ (ISid->IdentifierAuthority.Value[5] == NtAuthority.Value[5])
+ ) {
+
+ return(TRUE);
+ }
+ }
+ }
+
+ return(FALSE);
+
+}
+
+
+VOID
+LsapAuSetLogonPrivilegeStates(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG PrivilegeCount,
+ IN PLUID_AND_ATTRIBUTES Privileges
+ )
+/*++
+
+Routine Description:
+
+ This is an interesting routine. Its purpose is to establish the
+ intial state (enabled/disabled) of privileges. This information
+ comes from LSA, but we need to over-ride that information for the
+ time being based upon logon type.
+
+ Basically, without dynamic context tracking supported across the
+ network, network logons have no way to enable privileges. Therefore,
+ we will enable all privileges for network logons.
+
+ For interactive, service, and batch logons, the programs or utilities
+ used are able to enable privileges when needed. Therefore, privileges
+ for these logon types will be disabled.
+
+ Despite the rules above, the SeChangeNotifyPrivilege will ALWAYS
+ be enabled if granted to a user (even for interactive, service, and
+ batch logons).
+
+
+Arguments:
+
+ PrivilegeCount - The number of privileges being assigned for this
+ logon.
+
+ Privileges - The privileges, and their attributes, being assigned
+ for this logon.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+
+ ULONG
+ i,
+ NewAttributes;
+
+ LUID
+ ChangeNotify;
+
+
+ //
+ // Enable or disable all privileges according to logon type
+ //
+
+ if (LogonType == Network) {
+ NewAttributes = (SE_PRIVILEGE_ENABLED_BY_DEFAULT |
+ SE_PRIVILEGE_ENABLED);
+ } else {
+ NewAttributes = 0;
+ }
+
+
+ for (i=0; i<PrivilegeCount; i++) {
+ Privileges[i].Attributes = NewAttributes;
+ }
+
+
+
+ //
+ // Interactive, Service, and Batch need to have the
+ // SeChangeNotifyPrivilege enabled. Network already
+ // has it enabled.
+ //
+
+ if (LogonType == Network) {
+ return;
+ }
+
+
+ ChangeNotify = RtlConvertLongToLuid(SE_CHANGE_NOTIFY_PRIVILEGE);
+
+ for ( i=0; i<PrivilegeCount; i++) {
+ if (RtlEqualLuid(&Privileges[i].Luid, &ChangeNotify) == TRUE) {
+ Privileges[i].Attributes = (SE_PRIVILEGE_ENABLED_BY_DEFAULT |
+ SE_PRIVILEGE_ENABLED);
+ }
+ }
+
+ return;
+
+}
diff --git a/private/lsa/server/auinit.c b/private/lsa/server/auinit.c
new file mode 100644
index 000000000..3536a6a91
--- /dev/null
+++ b/private/lsa/server/auinit.c
@@ -0,0 +1,329 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ auinit.c
+
+Abstract:
+
+ This module performs initialization of the authentication aspects
+ of the lsa.
+
+Author:
+
+ Jim Kelly (JimK) 26-February-1991
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "ausrvp.h"
+#include <string.h>
+
+//
+// Internal routine prototypes
+//
+
+
+NTSTATUS
+LsapBuildWorldSynchSD(
+ IN PSECURITY_DESCRIPTOR SD,
+ IN PACL Dacl,
+ IN ULONG AclLength
+ );
+
+
+
+
+
+
+BOOLEAN
+LsapAuInit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the LSA authentication services.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE EventHandle;
+ UNICODE_STRING UnicodeName;
+
+ SECURITY_DESCRIPTOR WorldSynchSD;
+ CHAR WorldSynchDaclBuffer[200]; //200 bytes is plenty for this.
+ PACL WorldSynchDacl = (PACL)(WorldSynchDaclBuffer);
+ LUID SystemLuid = SYSTEM_LUID;
+
+ LsapSystemLogonId.LowPart = SystemLuid.LowPart;
+ LsapSystemLogonId.HighPart = SystemLuid.HighPart;
+
+ //
+ // Strings needed for auditing.
+ //
+
+ RtlInitUnicodeString( &LsapLsaAuName, L"NT Local Security Authority / Authentication Service" );
+ RtlInitUnicodeString( &LsapRegisterLogonServiceName, L"LsaRegisterLogonProcess()" );
+
+ RtlInitializeCriticalSection(&LsapAuLock);
+
+ if (!LsapEnableCreateTokenPrivilege() ) {
+ return FALSE;
+ }
+
+
+ if (!LsapLogonSessionInitialize() ) {
+ return FALSE;
+ }
+
+
+ if (!LsapPackageInitialize() ) {
+ return FALSE;
+ }
+
+
+ if (!LsapAuLoopInitialize()) {
+ return FALSE;
+ }
+
+
+ //
+ // Initialize the logon process context management services
+ //
+
+ Status = LsapAuInitializeContextMgr();
+ ASSERT(NT_SUCCESS(Status));
+ if (!NT_SUCCESS(Status)) {
+ return(FALSE);
+ }
+
+
+
+ //
+ // Indicate that we are ready to accept LSA authentication
+ // service requests. Allow anyone to wait on this event.
+ //
+ // NOTE: This must be done even if authentication is not
+ // active in the system. Otherwise logon processes
+ // won't know when to query the authentication state.
+ //
+
+ Status = LsapBuildWorldSynchSD( &WorldSynchSD, WorldSynchDacl, sizeof(WorldSynchDaclBuffer) );
+ RtlInitUnicodeString( &UnicodeName, L"\\SECURITY\\LSA_AUTHENTICATION_INITIALIZED" );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ &WorldSynchSD
+ );
+
+ Status = NtOpenEvent( &EventHandle, GENERIC_WRITE, &ObjectAttributes );
+ ASSERTMSG("LSA/AU Initialization Notification Event Open Failed.",NT_SUCCESS(Status));
+
+ Status = NtSetEvent( EventHandle, NULL );
+ ASSERTMSG("LSA/AU Initialization Notification Failed.",NT_SUCCESS(Status));
+
+ Status = NtClose( EventHandle );
+ ASSERTMSG("LSA/AU Initialization Notification Event Closure Failed.",NT_SUCCESS(Status));
+
+ return TRUE;
+
+}
+
+
+
+BOOLEAN
+LsapEnableCreateTokenPrivilege(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function enabled the SeCreateTokenPrivilege privilege.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if privilege successfully enabled.
+ FALSE if not successfully enabled.
+
+--*/
+{
+
+ NTSTATUS Status;
+ HANDLE Token;
+ LUID CreateTokenPrivilege;
+ PTOKEN_PRIVILEGES NewState;
+ ULONG ReturnLength;
+
+
+ //
+ // Open our own token
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES,
+ &Token
+ );
+ ASSERTMSG( "LSA/AU Cant open own process token.", NT_SUCCESS(Status) );
+
+
+ //
+ // Initialize the adjustment structure
+ //
+
+ CreateTokenPrivilege =
+ RtlConvertLongToLuid(SE_CREATE_TOKEN_PRIVILEGE);
+
+ ASSERT( (sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) < 100);
+ NewState = LsapAllocateLsaHeap( 100 );
+
+ NewState->PrivilegeCount = 1;
+ NewState->Privileges[0].Luid = CreateTokenPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+
+ //
+ // Set the state of the privilege to ENABLED.
+ //
+
+ Status = NtAdjustPrivilegesToken(
+ Token, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+ ASSERTMSG("LSA/AU Cant enable CreateTokenPrivilege.", NT_SUCCESS(Status) );
+
+
+ //
+ // Clean up some stuff before returning
+ //
+
+ LsapFreeLsaHeap( NewState );
+ Status = NtClose( Token );
+ ASSERTMSG("LSA/AU Cant close process token.", NT_SUCCESS(Status) );
+
+
+ return TRUE;
+
+}
+
+
+NTSTATUS
+LsapBuildWorldSynchSD(
+ IN PSECURITY_DESCRIPTOR SD,
+ IN PACL Dacl,
+ IN ULONG AclLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds an absolute security descriptor containing an
+ ACL granting WORLD:SYNCHRONIZE access.
+
+Arguments:
+
+ SD - Pointer to the security descriptor to be initialized.
+
+ Dacl - Pointer to the ACL to be initialized.
+
+ AclLength - Length of the buffer pointed to by ACL.
+
+Return Value:
+
+ STATUS_SUCCESS - The security desciptor has been initialized.
+
+ STATUS_BUFFER_TOO_SMALL - The ACL buffer is not large enough to build the ACL.
+
+--*/
+{
+ NTSTATUS
+ Status;
+
+ ULONG
+ Length;
+
+
+ ASSERT(SD != NULL);
+ ASSERT(Dacl != NULL);
+
+ //
+ // Initialize the security descriptor.
+ // This call should not fail.
+ //
+
+ Status = RtlCreateSecurityDescriptor( SD, SECURITY_DESCRIPTOR_REVISION1 );
+ ASSERT(NT_SUCCESS(Status));
+
+ Length = (ULONG)sizeof(ACL) +
+ ((ULONG)sizeof(ACCESS_ALLOWED_ACE)) +
+ RtlLengthSid( LsapWorldSid );
+
+ if (AclLength < Length) {
+ return(STATUS_BUFFER_TOO_SMALL);
+ }
+
+
+ Status = RtlCreateAcl (Dacl, Length, ACL_REVISION2 );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Add ACEs to the ACL...
+ // These calls should not be able to fail.
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ Dacl,
+ ACL_REVISION2,
+ (SYNCHRONIZE ),
+ LsapWorldSid
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // And add the ACL to the security descriptor.
+ // This call should not fail.
+ //
+
+ Status = RtlSetDaclSecurityDescriptor(
+ SD,
+ TRUE, // DaclPresent
+ Dacl, // Dacl
+ FALSE // DaclDefaulted
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ return(STATUS_SUCCESS);
+}
diff --git a/private/lsa/server/aulogon.c b/private/lsa/server/aulogon.c
new file mode 100644
index 000000000..8b501b788
--- /dev/null
+++ b/private/lsa/server/aulogon.c
@@ -0,0 +1,2120 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ aulogon.c
+
+Abstract:
+
+ This module provides the dispatch code for LsaLogonUser() and
+ related logon support routines.
+
+ This file does NOT include the LSA Filter/Augmentor logic.
+
+Author:
+
+ Jim Kelly (JimK) 11-Mar-1992
+
+Revision History:
+
+--*/
+
+#include <msaudite.h>
+#include "lsasrvp.h"
+#include "ausrvp.h"
+#include "adtp.h"
+#include "ntlsapi.h"
+
+
+//
+// Pointer to license server routines in ntlsapi.dll
+//
+PNT_LICENSE_REQUEST_W LsaNtLicenseRequestW = NULL;
+PNT_LS_FREE_HANDLE LsaNtLsFreeHandle = NULL;
+
+
+
+
+
+NTSTATUS
+LsaCallLicenseServer(
+ IN PWCHAR LogonProcessName,
+ IN PUNICODE_STRING AccountName,
+ IN PUNICODE_STRING DomainName OPTIONAL,
+ IN BOOLEAN IsAdmin,
+ OUT HANDLE *LicenseHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function loads the license server DLL and calls it to indicate the
+ specified logon process has successfully authenticated the specified user.
+
+Arguments:
+
+ LogonProcessName - Name of the process authenticating the user.
+
+ AccountName - Name of the account authenticated.
+
+ DomainName - Name of the domain containing AccountName
+
+ IsAdmin - TRUE if the logged on user is an administrator
+
+ LicenseHandle - Returns a handle to the LicenseServer that must be
+ closed when the session goes away. INVALID_HANDLE_VALUE is returned
+ if the handle need not be closed.
+
+Return Value:
+
+ None.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ NT_LS_DATA NtLsData;
+ ULONG BufferSize;
+ LPWSTR Name;
+ LS_STATUS_CODE LsStatus;
+ LS_HANDLE LsHandle;
+
+ static enum {
+ FirstCall,
+ DllMissing,
+ DllLoaded } DllState = FirstCall ;
+
+ HINSTANCE DllHandle;
+
+
+ //
+ // Initialization
+ //
+
+ NtLsData.DataType = NT_LS_USER_NAME;
+ NtLsData.Data = NULL;
+ NtLsData.IsAdmin = IsAdmin;
+ *LicenseHandle = INVALID_HANDLE_VALUE;
+
+
+ //
+ // Load the license server DLL if this is the first call to this routine.
+ //
+
+ LsapAuLock();
+
+ if ( DllState == FirstCall ) {
+
+ //
+ // Load the DLL
+ //
+
+ DllHandle = LoadLibraryA( "ntlsapi" );
+
+ if ( DllHandle == NULL ) {
+ LsapAuUnlock();
+ DllState = DllMissing;
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ //
+ // Find the License routine
+ //
+
+
+ LsaNtLicenseRequestW = (PNT_LICENSE_REQUEST_W)
+ GetProcAddress(DllHandle, "NtLicenseRequestW");
+
+ if ( LsaNtLicenseRequestW == NULL ) {
+ LsapAuUnlock();
+ DllState = DllMissing;
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ //
+ // Find the License handle free routine
+ //
+
+
+ LsaNtLsFreeHandle = (PNT_LS_FREE_HANDLE)
+ GetProcAddress(DllHandle, "NtLSFreeHandle");
+
+ if ( LsaNtLsFreeHandle == NULL ) {
+ LsapAuUnlock();
+ DllState = DllMissing;
+ *LsaNtLicenseRequestW = NULL;
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ DllState = DllLoaded;
+
+ //
+ // Ensure the Dll was loaded on a previous call
+ //
+ } else if ( DllState != DllLoaded ) {
+ LsapAuUnlock();
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ LsapAuUnlock();
+
+
+
+ //
+ // Allocate a buffer for the combined DomainName\UserName
+ //
+
+ BufferSize = AccountName->Length + sizeof(WCHAR);
+ if ( DomainName != NULL && DomainName->Length != 0 ) {
+ BufferSize += DomainName->Length + sizeof(WCHAR);
+ }
+
+ NtLsData.Data = LsapAllocateLsaHeap( BufferSize );
+
+ if ( NtLsData.Data == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ //
+ // Fill in the DomainName\UserName
+ //
+
+ Name = (LPWSTR)(NtLsData.Data);
+
+ if ( DomainName != NULL && DomainName->Length != 0 ) {
+ RtlCopyMemory( Name,
+ DomainName->Buffer,
+ DomainName->Length );
+ Name += DomainName->Length / sizeof(WCHAR);
+ *Name = L'\\';
+ Name++;
+ }
+
+ RtlCopyMemory( Name,
+ AccountName->Buffer,
+ AccountName->Length );
+ Name += AccountName->Length / sizeof(WCHAR);
+ *Name = L'\0';
+
+
+ //
+ // Call the license server.
+ //
+
+ LsStatus = (*LsaNtLicenseRequestW)(
+ LogonProcessName,
+ NULL,
+ &LsHandle,
+ &NtLsData );
+
+ switch (LsStatus) {
+ case LS_SUCCESS:
+ Status = STATUS_SUCCESS;
+ *LicenseHandle = (HANDLE) LsHandle;
+ break;
+
+ case LS_INSUFFICIENT_UNITS:
+ Status = STATUS_LICENSE_QUOTA_EXCEEDED;
+ break;
+
+ case LS_RESOURCES_UNAVAILABLE:
+ Status = STATUS_NO_MEMORY;
+ break;
+
+ default:
+ //
+ // Unavailability of the license server isn't fatal.
+ //
+ Status = STATUS_SUCCESS;
+ break;
+ }
+
+
+
+ //
+ // Cleanup and return.
+ //
+Cleanup:
+ if ( NtLsData.Data != NULL ) {
+ LsapFreeLsaHeap( NtLsData.Data );
+ }
+
+ return Status;
+}
+
+
+
+
+VOID
+LsaFreeLicenseHandle(
+ IN HANDLE LicenseHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Free a handle returned by LsaCallLicenseServer.
+
+Arguments:
+
+ LicenseHandle - Handle returned to license for this logon session.
+
+Return Value:
+
+ None.
+
+
+--*/
+
+{
+ if ( LsaNtLsFreeHandle != NULL && LicenseHandle != INVALID_HANDLE_VALUE ) {
+ LS_HANDLE LsHandle;
+ LsHandle = (LS_HANDLE) LicenseHandle;
+ (*LsaNtLsFreeHandle)( LsHandle );
+ }
+}
+
+
+
+NTSTATUS
+LsapAuApiDispatchLogonUser(
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the dispatch routine for LsaLogonUser().
+
+Arguments:
+
+ Request - Represents the client's LPC request message and context.
+ The request message contains a LSAP_LOGON_USER_ARGS message
+ block.
+
+Return Value:
+
+ In addition to the status values that an authentication package
+ might return, this routine will return the following:
+
+ STATUS_NO_SUCH_PACKAGE - The specified authentication package is
+ unknown to the LSA.
+
+
+--*/
+
+{
+
+ NTSTATUS Status, TmpStatus, IgnoreStatus;
+ PLSAP_LOGON_USER_ARGS Arguments;
+ PLSA_PACKAGE_TABLE PackageApi;
+ PVOID LocalAuthenticationInformation; // Receives a copy of authentication information
+ PTOKEN_GROUPS ClientTokenGroups;
+ PVOID TokenInformation;
+ LSA_TOKEN_INFORMATION_TYPE TokenInformationType;
+ LSA_TOKEN_INFORMATION_TYPE OriginalTokenType;
+ PLSA_TOKEN_INFORMATION_V1 TokenInformationV1;
+ PLSA_TOKEN_INFORMATION_NULL TokenInformationNull;
+ HANDLE Token;
+ PUNICODE_STRING AccountName = NULL;
+ PUNICODE_STRING AuthenticatingAuthority = NULL;
+ PUNICODE_STRING SourceDevice = NULL;
+ PUNICODE_STRING WorkstationName = NULL;
+ PSID UserSid = NULL;
+ LUID AuthenticationId;
+ ANSI_STRING AnsiSourceContext;
+ CHAR AnsiBuffer[TOKEN_SOURCE_LENGTH + 2];
+ UNICODE_STRING UnicodeSourceContext;
+ WCHAR UnicodeBuffer[TOKEN_SOURCE_LENGTH + 2];
+ ULONG UserSidSize;
+ USHORT EventType;
+ USHORT EventCategory;
+ ULONG EventID;
+ NTSTATUS XStatus;
+ PSTRING PackageName;
+ UNICODE_STRING PackageNameU;
+ BOOLEAN FreePackageName = TRUE;
+ PPRIVILEGE_SET PrivilegesAssigned = NULL;
+ BOOLEAN CallLicenseServer;
+ SECURITY_LOGON_TYPE ActiveLogonType;
+
+ //
+ // Don't allow untrusted clients to call this API.
+ //
+
+ if (!TrustedClient) {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+
+ Arguments = &ClientRequest->Request->Arguments.LogonUser;
+
+ AnsiSourceContext.Buffer = NULL;
+ AnsiSourceContext.MaximumLength = AnsiSourceContext.Length = 0;
+
+ UnicodeSourceContext.Buffer = NULL;
+ UnicodeSourceContext.MaximumLength = UnicodeSourceContext.Length = 0;
+
+ //
+ // Determine if the LicenseServer should be called.
+ // Turn off the flag to prevent confusing any other logic below.
+ //
+
+ if ( Arguments->AuthenticationPackage & LSA_CALL_LICENSE_SERVER ) {
+ Arguments->AuthenticationPackage &= ~LSA_CALL_LICENSE_SERVER ;
+ CallLicenseServer = TRUE;
+ } else {
+ CallLicenseServer = FALSE;
+ }
+
+
+ //
+ // Map an unlock logon into an interactive logon
+ //
+
+ ActiveLogonType = Arguments->LogonType;
+ if (ActiveLogonType == Unlock) {
+ ActiveLogonType = Interactive;
+ }
+
+ //
+ // Get the address of the package to call
+ //
+
+ LsapAuLock();
+
+ if ( Arguments->AuthenticationPackage >= LsapPackageCount ) {
+ LsapAuUnlock();
+ return STATUS_NO_SUCH_PACKAGE;
+ }
+
+ PackageApi =
+ &LsapPackageArray->Package[Arguments->AuthenticationPackage]->PackageApi;
+
+ PackageName = LsapQueryPackageName( LsapPackageArray->Package[Arguments->AuthenticationPackage] );
+
+ LsapAuUnlock();
+
+ //
+ // This code should be removed when the Package name is turned
+ // into a unicode string.
+ //
+
+ Status = RtlAnsiStringToUnicodeString(
+ &PackageNameU,
+ (PANSI_STRING)PackageName,
+ TRUE
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &PackageNameU, L"-" );
+ FreePackageName = FALSE;
+ }
+
+
+ //
+ // Fetch a copy of the authentication information from the client's
+ // address space.
+ //
+
+ if (Arguments->AuthenticationInformationLength != 0) {
+
+ LocalAuthenticationInformation =
+ LsapAllocateLsaHeap( Arguments->AuthenticationInformationLength );
+ if (LocalAuthenticationInformation == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ Status = LsapCopyFromClientBuffer (
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->AuthenticationInformationLength,
+ LocalAuthenticationInformation,
+ Arguments->AuthenticationInformation
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ DbgPrint("LSA/LogonUser(): Failed to retrieve Auth. Info. %lx\n",Status);
+ return Status;
+ }
+
+ } else {
+ LocalAuthenticationInformation = NULL;
+ }
+
+
+ //
+ // Capture the local groups ( a rather complicated task ).
+ //
+
+ ClientTokenGroups = Arguments->LocalGroups; // Save so we can restore it later
+ Status = LsapCaptureClientTokenGroups(
+ ClientRequest,
+ Arguments->LocalGroupsCount,
+ ClientTokenGroups,
+ &Arguments->LocalGroups
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ DbgPrint("LSA/LogonUser(): Failed to retrieve local groups %lx\n",Status);
+ LsapFreeLsaHeap( LocalAuthenticationInformation );
+ return Status;
+ }
+
+
+
+ //
+ // Now call the package...
+ //
+ //
+ // Once the authentication package returns success from this
+ // call, it is LSA's responsibility to clean up the logon
+ // session when it is no longer needed. This is true whether
+ // the logon fails due to other constraints, or because the
+ // user ultimately logs off.
+ //
+
+ if (PackageApi->LsapApLogonUserEx != NULL) {
+
+ Status = (PackageApi->LsapApLogonUserEx)(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ ActiveLogonType,
+ LocalAuthenticationInformation,
+ Arguments->AuthenticationInformation, //client base
+ Arguments->AuthenticationInformationLength,
+ &Arguments->ProfileBuffer,
+ &Arguments->ProfileBufferLength,
+ &Arguments->LogonId,
+ &Arguments->SubStatus,
+ &TokenInformationType,
+ &TokenInformation,
+ &AccountName,
+ &AuthenticatingAuthority,
+ &WorkstationName
+ );
+ } else {
+
+ //
+ // We checked to make sure that at least one of these was exported
+ // from the package, so we know we can call this if LsapApLogonUserEx
+ // doesn't exist.
+ //
+
+ Status = (PackageApi->LsapApLogonUser)(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ ActiveLogonType,
+ LocalAuthenticationInformation,
+ Arguments->AuthenticationInformation, //client base
+ Arguments->AuthenticationInformationLength,
+ &Arguments->ProfileBuffer,
+ &Arguments->ProfileBufferLength,
+ &Arguments->LogonId,
+ &Arguments->SubStatus,
+ &TokenInformationType,
+ &TokenInformation,
+ &AccountName,
+ &AuthenticatingAuthority
+ );
+ }
+
+ //
+ // Free the local copy of the authentication information
+ //
+
+ if (LocalAuthenticationInformation != NULL) {
+ LsapFreeLsaHeap( LocalAuthenticationInformation );
+ }
+
+
+ AuthenticationId = Arguments->LogonId;
+
+ if ( !NT_SUCCESS(Status) ) {
+ LsapFreeTokenGroups( Arguments->LocalGroups );
+ Arguments->LocalGroups = ClientTokenGroups; // Restore to client's value
+ LsapFreeClientBuffer(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->ProfileBuffer
+ );
+ Arguments->ProfileBuffer = NULL;
+
+ goto Done;
+ }
+
+ OriginalTokenType = TokenInformationType;
+
+
+ //
+ // Incorporate any specified local groups into the token
+ // information. Note that the local group SIDs are referenced
+ // by the new copy of the TokenInformation, and so may not be
+ // deallocated. The SIDs will be deallocated when the TokenInformation
+ // structure is deallocated.
+ //
+
+ if ( Arguments->LocalGroupsCount > 0) {
+ Status = LsapIncorporateLocalGroups(
+ Arguments->LocalGroups,
+ &TokenInformationType,
+ &TokenInformation
+ );
+ LsapFreeLsaHeap( Arguments->LocalGroups ); // But don't free individual SIDs
+ }
+ else {
+ Status = STATUS_SUCCESS;
+ }
+ Arguments->LocalGroups = ClientTokenGroups; // Restore to client's value
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // Pass the token information through the Local Security Policy
+ // Filter/Augmentor. This may cause some or all of the token
+ // information to be replaced/augmented.
+ //
+
+ Status = LsapAuUserLogonPolicyFilter(
+ ActiveLogonType,
+ &TokenInformationType,
+ &TokenInformation,
+ &Arguments->Quotas,
+ &PrivilegesAssigned
+ );
+
+ }
+
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Notify the logon package so it can clean up its
+ // logon session information.
+ //
+
+ (PackageApi->LsapApLogonTerminated)( &Arguments->LogonId );
+
+ //
+ // And delete the logon session
+ //
+
+ IgnoreStatus = LsapDeleteLogonSession( &Arguments->LogonId );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ //
+ // Free up the TokenInformation buffer and ProfileBuffer
+ // and return the error.
+ //
+
+ IgnoreStatus =
+ LsapFreeClientBuffer(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->ProfileBuffer
+ );
+ Arguments->ProfileBuffer = NULL;
+
+ switch ( TokenInformationType ) {
+ case LsaTokenInformationNull:
+ LsapFreeTokenInformationNull(
+ (PLSA_TOKEN_INFORMATION_NULL)TokenInformation
+ );
+ break;
+
+
+ case LsaTokenInformationV1:
+ LsapFreeTokenInformationV1(
+ (PLSA_TOKEN_INFORMATION_V1)TokenInformation
+ );
+ break;
+
+ }
+
+ goto Done;
+ }
+
+ //
+ // Check if we only allow admins to logon. We do allow null session
+ // connections since they are severly restricted, though. Since the
+ // token type may have been changed, we use the token type originally
+ // returned by the package.
+ //
+
+ if (LsapAllowAdminLogonsOnly &&
+ (OriginalTokenType == LsaTokenInformationV1) &&
+ ((((PLSA_TOKEN_INFORMATION_V1) TokenInformation)->Owner.Owner == NULL) ||
+ !RtlEqualSid(
+ ((PLSA_TOKEN_INFORMATION_V1) TokenInformation)->Owner.Owner,
+ LsapAliasAdminsSid
+ ) ) ) {
+
+ //
+ // Set the status to be invalid workstation, since all accounts
+ // except administrative ones are locked out for this
+ // workstation.
+ //
+
+ Arguments->SubStatus = STATUS_INVALID_WORKSTATION;
+ Status = STATUS_ACCOUNT_RESTRICTION;
+ //
+ // Notify the logon package so it can clean up its
+ // logon session information.
+ //
+
+ (PackageApi->LsapApLogonTerminated)( &Arguments->LogonId );
+
+ //
+ // And delete the logon session
+ //
+
+ IgnoreStatus = LsapDeleteLogonSession( &Arguments->LogonId );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ //
+ // Free up the TokenInformation buffer and ProfileBuffer
+ // and return the error.
+ //
+
+ IgnoreStatus =
+ LsapFreeClientBuffer(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->ProfileBuffer
+ );
+
+ switch ( TokenInformationType ) {
+ case LsaTokenInformationNull:
+ LsapFreeTokenInformationNull(
+ (PLSA_TOKEN_INFORMATION_NULL)TokenInformation
+ );
+ break;
+
+
+ case LsaTokenInformationV1:
+ LsapFreeTokenInformationV1(
+ (PLSA_TOKEN_INFORMATION_V1)TokenInformation
+ );
+ break;
+
+ }
+
+ goto Done;
+ }
+ //
+ // Call the LicenseServer
+ //
+
+ if ( CallLicenseServer ) {
+
+ PLSAP_LOGON_SESSION LogonSession;
+ HANDLE LicenseHandle;
+ BOOLEAN IsAdmin = FALSE;
+
+ //
+ // Determine if we're logged on as administrator.
+ //
+ if ( TokenInformationType == LsaTokenInformationV1 &&
+ ((PLSA_TOKEN_INFORMATION_V1)TokenInformation)->Owner.Owner != NULL &&
+ RtlEqualSid(
+ ((PLSA_TOKEN_INFORMATION_V1)TokenInformation)->Owner.Owner,
+ LsapAliasAdminsSid ) ) {
+
+ IsAdmin = TRUE;
+
+ }
+
+ //
+ // Call the license server.
+ //
+
+ Status = LsaCallLicenseServer(
+ ClientRequest->LogonProcessContext->LogonProcessName,
+ AccountName,
+ AuthenticatingAuthority,
+ IsAdmin,
+ &LicenseHandle );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Notify the logon package so it can clean up its
+ // logon session information.
+ //
+
+ (PackageApi->LsapApLogonTerminated)( &Arguments->LogonId );
+
+ //
+ // And delete the logon session
+ //
+
+ IgnoreStatus = LsapDeleteLogonSession( &Arguments->LogonId );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ //
+ // Free up the TokenInformation buffer and ProfileBuffer
+ // and return the error.
+ //
+
+ IgnoreStatus =
+ LsapFreeClientBuffer(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->ProfileBuffer
+ );
+ Arguments->ProfileBuffer = NULL;
+
+
+ switch ( TokenInformationType ) {
+ case LsaTokenInformationNull:
+ LsapFreeTokenInformationNull(
+ (PLSA_TOKEN_INFORMATION_NULL)TokenInformation
+ );
+ break;
+
+
+ case LsaTokenInformationV1:
+ LsapFreeTokenInformationV1(
+ (PLSA_TOKEN_INFORMATION_V1)TokenInformation
+ );
+ break;
+
+ }
+
+ goto Done;
+ }
+
+ //
+ // Save the LicenseHandle in the LogonSession so we can close the
+ // handle on logoff.
+ //
+ LsapAuLock();
+ LogonSession = LsapGetLogonSession ( &Arguments->LogonId, FALSE );
+
+ if ( LogonSession != NULL ) {
+ LogonSession->LicenseHandle = LicenseHandle;
+ }
+ LsapAuUnlock();
+
+ //
+ // If we couldn't save the handle,
+ // close it now.
+ //
+ if ( LogonSession == NULL ) {
+ LsaFreeLicenseHandle( LicenseHandle );
+ }
+
+ }
+
+
+
+ //
+ // Case on the token information returned (and subsequently massaged)
+ // to create the correct kind of token.
+ //
+
+ switch (TokenInformationType) {
+
+ case LsaTokenInformationNull:
+
+ TokenInformationNull = TokenInformation;
+
+ //
+ // The user hasn't logged on to any particular account.
+ // An impersonation token with WORLD as owner
+ // will be created.
+ //
+
+
+ Status = LsapCreateNullToken(
+ &Arguments->LogonId,
+ &Arguments->SourceContext,
+ TokenInformationNull,
+ &Token
+ );
+
+
+ //
+ // Deallocate all the heap that was passed back from the
+ // authentication package via the TokenInformation buffer.
+ //
+
+ UserSid = NULL;
+
+ LsapFreeTokenInformationNull( TokenInformationNull );
+
+
+ break;
+
+
+
+
+ case LsaTokenInformationV1:
+
+ TokenInformationV1 = TokenInformation;
+
+ //
+ // the type of token created depends upon the type of logon
+ // being requested:
+ //
+ // InteractiveLogon => PrimaryToken
+ // BatchLogon => PrimaryToken
+ // NetworkLogon => ImpersonationToken
+ //
+
+ if (ActiveLogonType != Network) {
+
+ //
+ // Primary token
+ //
+
+ Status = LsapCreateV1Token(
+ &Arguments->LogonId,
+ &Arguments->SourceContext,
+ TokenInformationV1,
+ TokenPrimary,
+ &Token
+ );
+
+
+ } else {
+
+ //
+ // Impersonation token
+ //
+
+ Status = LsapCreateV1Token(
+ &Arguments->LogonId,
+ &Arguments->SourceContext,
+ TokenInformationV1,
+ TokenImpersonation,
+ &Token
+ );
+
+
+ }
+
+
+ //
+ // Copy out the User Sid
+ //
+
+ if ( NT_SUCCESS( Status )) {
+
+ UserSidSize = RtlLengthSid( TokenInformationV1->User.User.Sid );
+
+ UserSid = LsapAllocateLsaHeap( UserSidSize );
+
+ RtlCopySid( UserSidSize, UserSid, TokenInformationV1->User.User.Sid );
+ }
+
+
+
+ //
+ // Deallocate all the heap that was passed back from the
+ // authentication package via the TokenInformation buffer.
+ //
+
+ LsapFreeTokenInformationV1( TokenInformationV1 );
+
+ break;
+
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Notify the logon package so it can clean up its
+ // logon session information.
+ //
+
+ (PackageApi->LsapApLogonTerminated)( &Arguments->LogonId );
+
+ //
+ // And delete the logon session
+ //
+
+ IgnoreStatus = LsapDeleteLogonSession( &Arguments->LogonId );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ IgnoreStatus =
+ LsapFreeClientBuffer(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->ProfileBuffer
+ );
+ Arguments->ProfileBuffer = NULL;
+
+ goto Done;
+
+ }
+
+
+
+
+ //
+ // Duplicate the token handle back into the calling process
+ //
+
+ Status = NtDuplicateObject(
+ NtCurrentProcess(),
+ Token,
+ ClientRequest->LogonProcessContext->ClientProcess,
+ &Arguments->Token,
+ 0, // Ignored desired access
+ 0, // Handle attributes
+ DUPLICATE_SAME_ACCESS |
+ DUPLICATE_CLOSE_SOURCE
+ );
+
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Notify the logon package so it can clean up its
+ // logon session information.
+ //
+
+ (PackageApi->LsapApLogonTerminated)( &Arguments->LogonId );
+
+ //
+ // We use to close the token handle here.
+ // However, NtDuplicateObject() closes the handle in most
+ // unusual error situations (those that aren't bad parameter
+ // oriented) if you specify DUPLICATE_CLOSE_SOURCE. So,
+ // now we don't close the handle on error.
+ //
+
+ // IgnoreStatus = NtClose( Token );
+ // ASSERT( NT_SUCCESS(Token) );
+
+ IgnoreStatus =
+ LsapFreeClientBuffer(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->ProfileBuffer
+ );
+ Arguments->ProfileBuffer = NULL;
+
+ goto Done;
+
+ }
+
+
+Done:
+
+
+ //
+ // Audit the logon attempt. The event type and logged information
+ // will depend to some extent on the whether we failed and why.
+ //
+
+ //
+ // Turn the SourceContext into something we can
+ // work with.
+ //
+
+ AnsiSourceContext.Buffer = AnsiBuffer;
+ RtlCopyMemory(
+ AnsiBuffer,
+ Arguments->SourceContext.SourceName,
+ TOKEN_SOURCE_LENGTH * sizeof( CHAR )
+ );
+ AnsiBuffer[TOKEN_SOURCE_LENGTH] = '\0';
+
+ AnsiSourceContext.Length = strlen(AnsiBuffer);
+ AnsiSourceContext.MaximumLength = (TOKEN_SOURCE_LENGTH + 2) * sizeof( CHAR );
+
+ UnicodeSourceContext.Buffer = UnicodeBuffer;
+ UnicodeSourceContext.MaximumLength = (TOKEN_SOURCE_LENGTH + 2) * sizeof( WCHAR );
+
+
+ XStatus = RtlAnsiStringToUnicodeString(
+ &UnicodeSourceContext,
+ &AnsiSourceContext,
+ FALSE
+ );
+
+ if ( !NT_SUCCESS( XStatus )) {
+
+ UnicodeSourceContext.Buffer = NULL;
+ }
+
+ //
+ // Assume the logon failed, reset if necessary.
+ //
+
+ EventCategory = SE_CATEGID_LOGON;
+ EventType = EVENTLOG_AUDIT_FAILURE;
+
+
+ switch ( Status ) {
+ case STATUS_SUCCESS:
+
+ {
+
+ EventID = SE_AUDITID_SUCCESSFUL_LOGON;
+ EventType = EVENTLOG_AUDIT_SUCCESS;
+
+ break;
+ }
+ case STATUS_BAD_VALIDATION_CLASS:
+
+ {
+ EventID = SE_AUDITID_UNSUCCESSFUL_LOGON;
+
+ break;
+
+ }
+
+ case STATUS_ACCOUNT_EXPIRED:
+
+ {
+ EventID = SE_AUDITID_ACCOUNT_EXPIRED;
+
+ break;
+
+ }
+
+ case STATUS_NETLOGON_NOT_STARTED:
+
+ {
+ EventID = SE_AUDITID_NETLOGON_NOT_STARTED;
+
+ break;
+
+ }
+
+ case STATUS_ACCOUNT_LOCKED_OUT:
+
+ {
+ EventID = SE_AUDITID_ACCOUNT_LOCKED;
+
+ break;
+
+ }
+
+ case STATUS_LOGON_TYPE_NOT_GRANTED:
+
+ {
+ EventID = SE_AUDITID_LOGON_TYPE_RESTR;
+
+ break;
+
+ }
+
+
+ case STATUS_ACCOUNT_RESTRICTION:
+
+ {
+
+ switch ( Arguments->SubStatus ) {
+ case STATUS_PASSWORD_EXPIRED:
+ {
+ EventID = SE_AUDITID_PASSWORD_EXPIRED;
+
+ break;
+ }
+ case STATUS_ACCOUNT_DISABLED:
+ {
+ EventID = SE_AUDITID_ACCOUNT_DISABLED;
+
+ break;
+ }
+ case STATUS_INVALID_LOGON_HOURS:
+ {
+ EventID = SE_AUDITID_ACCOUNT_TIME_RESTR;
+
+ break;
+ }
+ case STATUS_INVALID_WORKSTATION:
+
+ {
+ EventID = SE_AUDITID_WORKSTATION_RESTR;
+
+ break;
+ }
+
+ default:
+ {
+ EventID = SE_AUDITID_UNKNOWN_USER_OR_PWD;
+ break;
+ }
+
+
+ }
+
+ break;
+ }
+
+ case STATUS_LOGON_FAILURE:
+
+ {
+ if ( ( Arguments->SubStatus == STATUS_WRONG_PASSWORD ) ||
+ ( Arguments->SubStatus == STATUS_NO_SUCH_USER )
+ ) {
+
+ EventID = SE_AUDITID_UNKNOWN_USER_OR_PWD;
+
+ //
+ // Blow away the substatus, we don't want it to
+ // get back to our caller.
+ //
+
+ Arguments->SubStatus = STATUS_SUCCESS;
+
+ } else {
+
+ EventID = SE_AUDITID_UNSUCCESSFUL_LOGON;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ EventID = SE_AUDITID_UNSUCCESSFUL_LOGON;
+
+ break;
+
+ }
+ }
+
+ LsapAdtAuditLogon( EventCategory,
+ EventID,
+ EventType,
+ AccountName,
+ AuthenticatingAuthority,
+ &UnicodeSourceContext,
+ SourceDevice,
+ &PackageNameU,
+ Arguments->LogonType,
+ UserSid,
+ AuthenticationId,
+ Status,
+ WorkstationName
+ );
+
+
+
+ if ( FreePackageName ) {
+ RtlFreeUnicodeString( &PackageNameU );
+ }
+
+ //
+ // The WorkstationName is only used by the audit, free it here.
+ //
+
+ if (WorkstationName != NULL) {
+ if (WorkstationName->Buffer != NULL) {
+ LsapFreeLsaHeap( WorkstationName->Buffer );
+ }
+ LsapFreeLsaHeap( WorkstationName );
+ }
+
+ TmpStatus = STATUS_SUCCESS;
+
+ //
+ // Set the logon session names.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ TmpStatus = LsapSetLogonSessionAccountInfo(
+ &AuthenticationId,
+ AccountName,
+ AuthenticatingAuthority,
+ UserSid,
+ Arguments->LogonType
+ );
+
+ }
+
+ //
+ // If we already had an error, or we receive an error from setting the
+ // logon , free any buffers related to the logon session.
+ //
+
+ if ((!NT_SUCCESS(Status)) || (!NT_SUCCESS(TmpStatus))) {
+
+ if (AccountName != NULL) {
+ if (AccountName->Buffer != NULL) {
+ LsapFreeLsaHeap( AccountName->Buffer );
+ }
+ LsapFreeLsaHeap( AccountName );
+ }
+
+ if (AuthenticatingAuthority != NULL) {
+ if (AuthenticatingAuthority->Buffer != NULL) {
+ LsapFreeLsaHeap( AuthenticatingAuthority->Buffer );
+ }
+ LsapFreeLsaHeap( AuthenticatingAuthority );
+ }
+ }
+
+ //
+ // Audit special privilege assignment, if there were any
+ //
+
+ if ( PrivilegesAssigned != NULL ) {
+
+ //
+ // Examine the list of privileges being assigned, and
+ // audit special privileges as appropriate.
+ //
+
+ if ( NT_SUCCESS( Status )) {
+ LsapAdtAuditSpecialPrivileges( PrivilegesAssigned, AuthenticationId, UserSid );
+ }
+
+ MIDL_user_free( PrivilegesAssigned );
+ }
+
+ return Status;
+}
+
+
+
+
+PSTRING
+LsapQueryPackageName(
+ PLSAP_PACKAGE_CONTEXT Package
+ )
+{
+ return( Package->Name );
+}
+
+
+
+
+NTSTATUS
+LsapCreateNullToken(
+ IN PLUID LogonId,
+ IN PTOKEN_SOURCE TokenSource,
+ IN PLSA_TOKEN_INFORMATION_NULL TokenInformationNull,
+ OUT PHANDLE Token
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a token representing a null logon.
+
+Arguments:
+
+ LogonId - The logon ID to assign to the new token.
+
+ TokenSource - Points to the value to use as the source of the token.
+
+ TokenInformationNull - Information received from the authentication
+ package authorizing this logon.
+
+ Token - receives the new token's handle value. The token is opened
+ for TOKEN_ALL_ACCESS.
+
+
+Return Value:
+
+ The status value of the NtCreateToken() call.
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ TOKEN_USER UserId;
+ TOKEN_PRIMARY_GROUP PrimaryGroup;
+ TOKEN_GROUPS GroupIds;
+ TOKEN_PRIVILEGES Privileges;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE ImpersonationQos;
+
+
+
+ UserId.User.Sid = LsapWorldSid;
+ UserId.User.Attributes = 0;
+ GroupIds.GroupCount = 0;
+ Privileges.PrivilegeCount = 0;
+ PrimaryGroup.PrimaryGroup = LsapWorldSid;
+
+ //
+ // Set the object attributes to specify an Impersonation impersonation
+ // level.
+ //
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
+ ImpersonationQos.ImpersonationLevel = SecurityImpersonation;
+ ImpersonationQos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
+ ImpersonationQos.EffectiveOnly = TRUE;
+ ImpersonationQos.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ ObjectAttributes.SecurityQualityOfService = &ImpersonationQos;
+
+ Status = NtCreateToken(
+ Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &ObjectAttributes, // ObjectAttributes
+ TokenImpersonation, // TokenType
+ LogonId, // Authentication LUID
+ &TokenInformationNull->ExpirationTime,
+ // Expiration Time
+ &UserId, // User ID
+ &GroupIds, // Group IDs
+ &Privileges, // Privileges
+ NULL, // Owner
+ &PrimaryGroup, // Primary Group
+ NULL, // Default Dacl
+ TokenSource // TokenSource
+ );
+
+ return Status;
+
+}
+
+
+NTSTATUS
+LsapCreateV1Token(
+ IN PLUID LogonId,
+ IN PTOKEN_SOURCE TokenSource,
+ IN PLSA_TOKEN_INFORMATION_V1 TokenInformationV1,
+ IN TOKEN_TYPE TokenType,
+ OUT PHANDLE Token
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a token from the information in a
+ TOKEN_INFORMATION_V1 structure.
+
+Arguments:
+
+ LogonId - The logon ID to assign to the new token.
+
+ TokenSource - Points to the value to use as the source of the token.
+
+ TokenInformationV1 - Information received from the authentication
+ package authorizing this logon.
+
+ TokenType - The type of token (Primary or impersonation) to create.
+ If an impersonation token is to be created, then it will be given
+ a level of SecurityImpersonation.
+
+ Token - receives the new token's handle value. The token is opened
+ for TOKEN_ALL_ACCESS.
+
+
+Return Value:
+
+ The status value of the NtCreateToken() call.
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PTOKEN_OWNER Owner;
+ PTOKEN_DEFAULT_DACL Dacl;
+ TOKEN_PRIVILEGES NoPrivileges;
+ PTOKEN_PRIVILEGES Privileges;
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE ImpersonationQos;
+
+
+ //
+ // Set an appropriate Owner and DefaultDacl argument value
+ //
+
+ Owner = NULL;
+ if ( TokenInformationV1->Owner.Owner != NULL ) {
+ Owner = &TokenInformationV1->Owner;
+ }
+
+ Dacl = NULL;
+ if ( TokenInformationV1->DefaultDacl.DefaultDacl !=NULL ) {
+ Dacl = &TokenInformationV1->DefaultDacl;
+ }
+
+ if ( TokenInformationV1->Privileges == NULL ) {
+ Privileges = &NoPrivileges;
+ NoPrivileges.PrivilegeCount = 0;
+ } else {
+ Privileges = TokenInformationV1->Privileges;
+ }
+
+
+
+ //
+ // Create the token - The impersonation level is only looked at
+ // if the token type is TokenImpersonation.
+ //
+
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
+ ImpersonationQos.ImpersonationLevel = SecurityImpersonation;
+ ImpersonationQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ ImpersonationQos.EffectiveOnly = FALSE;
+ ImpersonationQos.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ ObjectAttributes.SecurityQualityOfService = &ImpersonationQos;
+
+ Status =
+ NtCreateToken(
+ Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &ObjectAttributes, // ObjectAttributes
+ TokenType, // TokenType
+ LogonId, // Authentication LUID
+ &TokenInformationV1->ExpirationTime, // Expiration Time
+ &TokenInformationV1->User, // User ID
+ TokenInformationV1->Groups, // Group IDs
+ Privileges, // Privileges
+ Owner, // Owner
+ &TokenInformationV1->PrimaryGroup, // Primary Group
+ Dacl, // Default Dacl
+ TokenSource // TokenSource
+ );
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+LsapCaptureClientTokenGroups(
+ IN PLSAP_CLIENT_REQUEST ClientRequest,
+ IN ULONG GroupCount,
+ IN PTOKEN_GROUPS ClientTokenGroups,
+ OUT PTOKEN_GROUPS *CapturedTokenGroups
+ )
+
+/*++
+
+Routine Description:
+
+ This function retrieves a copy of a TOKEN_GROUPS structure from a
+ client process.
+
+ This is a messy operation because it involves so many virtual memory
+ read requests. First the variable length TOKEN_GROUPS structure must
+ be retrieved. Then, for each SID, the SID header must be retrieved
+ so that the SubAuthorityCount can be used to calculate the length of
+ the SID, which is susequently retrieved.
+
+Arguments:
+
+ ClientRequest - Identifies the client.
+
+ GroupCount - Indicates the number of groups in the TOKEN_GROUPS.
+
+ ClientTokenGroups - Points to a TOKEN_GROUPS structure to be captured from
+ the client process.
+
+ CapturedTokenGroups - Receives a pointer to the captured token groups.
+
+Return Value:
+
+ STATUS_INSUFFICIENT_RESOURCES - Indicates not enough resources are
+ available to the LSA to handle the request right now.
+
+ Any status value returned by LsapCopyFromClientBuffer().
+
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ ULONG i, Length, RetrieveCount, SidHeaderLength;
+ PTOKEN_GROUPS LocalGroups;
+ PSID SidHeader, NextClientSid;
+
+
+ if ( GroupCount == 0) {
+ (*CapturedTokenGroups) = NULL;
+ return STATUS_SUCCESS;
+ }
+
+
+
+ //
+ // First the variable length TOKEN_GROUPS structure
+ // is retrieved.
+ //
+
+ Length = (ULONG)sizeof(TOKEN_GROUPS)
+ + GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES)
+ - ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES);
+
+ LocalGroups = LsapAllocateLsaHeap( Length );
+ (*CapturedTokenGroups) = LocalGroups;
+ if ( LocalGroups == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = LsapCopyFromClientBuffer (
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Length,
+ LocalGroups,
+ ClientTokenGroups
+ );
+
+
+ if (!NT_SUCCESS(Status) ) {
+ LsapFreeLsaHeap( LocalGroups );
+ return Status;
+ }
+
+
+
+ //
+ // Now retrieve each group
+ //
+
+ RetrieveCount = 0; // Used for cleanup, if necessary.
+ SidHeaderLength = RtlLengthRequiredSid( 0 );
+ SidHeader = LsapAllocateLsaHeap( SidHeaderLength );
+ if ( SidHeader == NULL ) {
+ LsapFreeLsaHeap( LocalGroups );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = STATUS_SUCCESS;
+ i = 0;
+ while ( i < LocalGroups->GroupCount ) {
+
+ //
+ // Retrieve the next SID header
+ //
+
+ NextClientSid = LocalGroups->Groups[i].Sid;
+ Status = LsapCopyFromClientBuffer (
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ SidHeaderLength,
+ SidHeader,
+ NextClientSid
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ break;
+ }
+
+ //
+ // and use the header information to get the whole SID
+ //
+
+ Length = RtlLengthSid( SidHeader );
+ LocalGroups->Groups[i].Sid = LsapAllocateLsaHeap( Length );
+
+ if ( LocalGroups->Groups[i].Sid == NULL ) {
+ Status == STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ } else {
+ RetrieveCount += 1;
+ }
+
+
+
+ Status = LsapCopyFromClientBuffer (
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Length,
+ LocalGroups->Groups[i].Sid,
+ NextClientSid
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ break;
+ }
+
+
+ i += 1;
+
+ }
+ LsapFreeLsaHeap( SidHeader );
+
+
+ if ( NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+
+
+ //
+ // There was a failure along the way.
+ // We need to deallocate what has already been allocated.
+ //
+
+ i = 0;
+ while ( i < RetrieveCount ) {
+ LsapFreeLsaHeap( LocalGroups->Groups[i].Sid );
+ i += 1;
+ }
+
+ LsapFreeLsaHeap( LocalGroups );
+
+ return Status;
+
+}
+
+
+VOID
+LsapFreeTokenGroups(
+ IN PTOKEN_GROUPS TokenGroups OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function frees the local groups of a logon user arguments buffer.
+ The local groups are expected to have been captured into the server
+ process.
+
+
+Arguments:
+
+ TokenGroups - Points to the TOKEN_GROUPS to be freed. This may be
+ NULL, allowing the caller to pass whatever was returned by
+ LsapCaptureClientTokenGroups() - even if there were no local
+ groups.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG i;
+
+ if ( !ARGUMENT_PRESENT(TokenGroups) ) {
+ return;
+ }
+
+
+ i = 0;
+ while ( i < TokenGroups->GroupCount ) {
+ LsapFreeLsaHeap( TokenGroups->Groups[i].Sid );
+ i += 1;
+ }
+
+ LsapFreeLsaHeap( TokenGroups );
+
+ return;
+
+}
+
+
+VOID
+LsapFreeTokenInformationNull(
+ IN PLSA_TOKEN_INFORMATION_NULL TokenInformationNull
+ )
+
+/*++
+
+Routine Description:
+
+ This function frees the allocated structures associated with a
+ LSA_TOKEN_INFORMATION_NULL data structure.
+
+
+Arguments:
+
+ TokenInformationNull - Pointer to the data structure to be released.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ LsapFreeTokenGroups( TokenInformationNull->Groups );
+ LsapFreeLsaHeap( TokenInformationNull );
+
+}
+
+
+VOID
+LsapFreeTokenInformationV1(
+ IN PLSA_TOKEN_INFORMATION_V1 TokenInformationV1
+ )
+
+/*++
+
+Routine Description:
+
+ This function frees the allocated structures associated with a
+ LSA_TOKEN_INFORMATION_V1 data structure.
+
+
+Arguments:
+
+ TokenInformationV1 - Pointer to the data structure to be released.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Free the user SID (a required field)
+ //
+
+
+ LsapFreeLsaHeap( TokenInformationV1->User.User.Sid );
+
+
+ //
+ // Free any groups present
+ //
+
+ LsapFreeTokenGroups( TokenInformationV1->Groups );
+
+
+
+ //
+ // Free the primary group.
+ // This is a required field, but it is freed only if non-NULL
+ // so this routine can be used by the filter routine while building
+ // a V1 token information structure.
+ //
+
+ if ( TokenInformationV1->PrimaryGroup.PrimaryGroup != NULL ) {
+ LsapFreeLsaHeap( TokenInformationV1->PrimaryGroup.PrimaryGroup );
+ }
+
+
+
+ //
+ // Free the privileges.
+ // If there are no privileges this field will be NULL.
+ //
+
+ if ( TokenInformationV1->Privileges != NULL ) {
+ LsapFreeLsaHeap( TokenInformationV1->Privileges );
+ }
+
+
+
+ //
+ // Free the owner SID, if one is present
+ //
+
+ if ( TokenInformationV1->Owner.Owner != NULL) {
+ LsapFreeLsaHeap( TokenInformationV1->Owner.Owner );
+ }
+
+
+
+
+ //
+ // Free the default DACL if one is present
+ //
+
+ if ( TokenInformationV1->DefaultDacl.DefaultDacl != NULL) {
+ LsapFreeLsaHeap( TokenInformationV1->DefaultDacl.DefaultDacl );
+ }
+
+
+
+ //
+ // Free the structure itself.
+ //
+
+ LsapFreeLsaHeap( TokenInformationV1 );
+
+
+}
+
+
+NTSTATUS
+LsapIncorporateLocalGroups(
+ IN PTOKEN_GROUPS LocalGroups OPTIONAL,
+ IN PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID *TokenInformation
+ )
+
+/*++
+
+Routine Description:
+
+ Add the specified groups to those already in the TokenInformation.
+ If necessary, this routine will deallocate the current TokenInformation
+ and return a new one.
+
+ NOTE: SIDs from both the LocalGroups and TokenInformation are referenced,
+ not copied.
+
+Arguments:
+
+ LocalGroups - Pointer to a set of groups to incorporate into the
+ groups in the TokenInformation argument. If there are none,
+ then return with no action.
+
+ TokenInformationType - On input, indicates the type of token information
+ being passed. On output, indicates the type of token information
+ returned. For some information types, routine may find it necessary
+ to make and return another type of TokenInformation.
+
+ TokenInformation - Points to a pointer to the token information. This
+ routine may find it necessary to deallocate the current token
+ information and return a new one in its place.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The service has completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - heap could not be allocated to house
+ the combination of the existing and new groups.
+
+--*/
+
+{
+
+ PLSA_TOKEN_INFORMATION_V1 TokenInformationV1;
+ PLSA_TOKEN_INFORMATION_NULL TokenInformationNull;
+ PTOKEN_GROUPS *CurrentGroups, NewGroups;
+ ULONG Length, GroupCount, i, j;
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Currently, all token information types include groups. //
+ // If one is introduced in the future that doesn't include //
+ // groups, then we may have to allocate and return a different //
+ // TokenInformation structure than the one we are passed. //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ if ( !ARGUMENT_PRESENT(LocalGroups) ) {
+ return STATUS_SUCCESS;
+ }
+
+ GroupCount = LocalGroups->GroupCount;
+
+ if (GroupCount == 0) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // For the time being, all token information types include groups.
+ // Case on the TokenInformationType to get the address of the
+ // corresponding TOKEN_GROUPS data structure and then do the rest
+ // in common.
+ //
+
+ switch ( (*TokenInformationType) ) {
+
+ case LsaTokenInformationNull:
+
+ TokenInformationNull = (*TokenInformation);
+ CurrentGroups = &TokenInformationNull->Groups;
+ break;
+
+ case LsaTokenInformationV1:
+
+ TokenInformationV1 = (*TokenInformation);
+ CurrentGroups = &TokenInformationV1->Groups;
+ break;
+
+ }
+
+
+ //
+ // If there are already groups in the TokenInformation, then
+ // add the local groups to them. Otherwise, just make a copy
+ // of the local groups and add them to the token information.
+ //
+
+ if ( (*CurrentGroups) == NULL ) {
+
+ //
+ // Just copy the LocalGroups structure and assign it
+ //
+
+ Length = (ULONG)sizeof(TOKEN_GROUPS)
+ + GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES)
+ - ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES);
+
+ NewGroups = LsapAllocateLsaHeap( Length );
+
+ if ( NewGroups == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+
+ i = 0;
+ while ( i < LocalGroups->GroupCount ) {
+ NewGroups->Groups[i] = LocalGroups->Groups[i];
+ i += 1;
+ }
+
+
+
+ //
+ // Assign the new groups (no old ones to deallocate)
+ //
+
+ (*CurrentGroups) = NewGroups;
+
+
+ } else {
+
+ //
+ // Figure out how many groups there are and allocate a new
+ // TOKEN_GROUPS structure large enough to handle it.
+ //
+
+ GroupCount += (*CurrentGroups)->GroupCount;
+ Length = (ULONG)sizeof(TOKEN_GROUPS)
+ + GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES)
+ - ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES);
+
+ NewGroups = LsapAllocateLsaHeap( Length );
+
+ if ( NewGroups == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Copy the current groups into the beginning of the new
+ // TOKEN_GROUPS structure
+ //
+
+
+ NewGroups->GroupCount = GroupCount;
+ i = 0;
+ while ( i < (*CurrentGroups)->GroupCount ) {
+ NewGroups->Groups[i] = (*CurrentGroups)->Groups[i];
+ i += 1;
+ }
+
+ //
+ // Now add the local groups to it.
+ //
+
+ j = 0;
+ while ( j < LocalGroups->GroupCount ) {
+ NewGroups->Groups[i] = LocalGroups->Groups[j];
+ i += 1;
+ j += 1;
+ }
+
+
+ //
+ // Deallocate the old TOKEN_GROUPS structure
+ //
+
+ LsapFreeLsaHeap( (*CurrentGroups) );
+
+
+ //
+ // And assign the new ones
+ //
+
+ (*CurrentGroups) = NewGroups;
+
+
+ }
+
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+
+VOID
+LsapAuLogonTerminatedPackages(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This function notifies all loaded authentication packages that a logon
+ session is about to be deleted. The reference monitor portion of the
+ logon session has already been deleted, and the LSA portion will be
+ immediately after this routine completes.
+
+ To protect themselves against each other, authentication packages should
+ assume that the logon session does not necessarily currently exist.
+ That is, if the authentication package goes to query information from the
+ logon session credential information, and finds no such logon session,
+ it may be due to an error in another authentication package.
+
+
+
+Arguments:
+
+ LogonId - The LUID of the logon session.
+
+
+Return Value:
+
+ None.
+
+
+--*/
+
+{
+
+ ULONG NextPackage, PackageCount;
+ PLSA_PACKAGE_TABLE PackageApi;
+
+
+ //
+ // Get the number of loaded packages.
+ //
+
+ LsapAuLock();
+ PackageCount = LsapPackageCount;
+ LsapAuUnlock();
+
+
+
+ //
+ // Look at each loaded package for a name match
+ //
+
+
+ NextPackage = 0;
+ while ( NextPackage < PackageCount ) {
+
+
+ //
+ // Now call the package...
+ //
+
+#ifdef LSAP_AU_TRACK_LOGONS
+ DbgPrint("Lsa (au): Logoff notification to package: %S\n",
+ LsapPackageArray->Package[NextPackage]->Name);
+#endif //LSAP_AU_TRACK_LOGONS
+
+ PackageApi = &LsapPackageArray->Package[NextPackage]->PackageApi;
+ (PackageApi->LsapApLogonTerminated)( LogonId );
+
+ NextPackage += 1;
+ }
+
+ return;
+
+}
diff --git a/private/lsa/server/auloop.c b/private/lsa/server/auloop.c
new file mode 100644
index 000000000..34b98384a
--- /dev/null
+++ b/private/lsa/server/auloop.c
@@ -0,0 +1,1500 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ auloop.c
+
+Abstract:
+
+ Local Security Authority LPC Port listener and API dispatcher.
+
+Author:
+
+ Jim Kelly (JimK) 7-Mar-1991
+
+Revision History:
+
+--*/
+
+#include "ausrvp.h"
+#include <windows.h>
+
+
+//
+// These variables are used to control the number of
+// threads used for processing Logon Process calls.
+// They have the following uses:
+//
+// LsapAuActiveThreads - Indicates how many threads are
+// currently active (both free and in-use) for processing
+// Logon Process calls.
+//
+// LsapAuFreeThreads - Indicates how many threads are available
+// to process LPC calls. When this is less than
+// LsapAuFreeThreadsGoal then we create another thread
+// (bounded by LsapAuMaximumThreads).
+//
+// LsapAuFreeThreadsGoal - Indicates how many threads should be kept
+// available to process new LPC calls. This is necessary to
+// prevent a deadlock with csrss.
+//
+// LsapAuMinimumThreads - Indicates the minimum number of
+// threads to have available for processing LogonProcess calls.
+// LsapAuActiveThreads should never be decremented
+// below this value.
+//
+// LsapAuMaximumThreads - Indicates the maximum number of
+// threads to create for processing LogonProcess calls.
+// LsapAuActiveThreads should never be incremented
+// above this value.
+//
+// LsapAuCallsToProcess - Controls how many LPC calls each
+// dynamic thread will serve before exiting. This is
+// used to prevent rampant thread creation/deletion races.
+//
+// Note: The following conditions may cause LsapAuActiveThreads
+// to go outside the Minimum/Maximum bounds:
+//
+// 1) Initialization - We may start out with fewer threads
+// than the minimum.
+//
+// 2) Race conditions at thread exit - we are sloppy (but
+// fast) in dealing with decrementing the Active count.
+//
+// 3) Changes in max or min values - obviously may make the
+// active count outside the max/min range.
+//
+// 4) Race conditions in thread activation - we are sloppy
+// (but fast) in determining exactly how many threads
+// are active and free. This may result in more threads
+// than expected, but won't result in fewer threads
+// than expected.
+//
+
+
+RTL_RESOURCE LsapAuThreadCountLock;
+LONG LsapAuActiveThreads;
+LONG LsapAuFreeThreads;
+LONG LsapAuFreeThreadsGoal;
+LONG LsapAuMinimumThreads;
+LONG LsapAuMaximumThreads;
+LONG LsapAuCallsToProcess;
+
+
+
+//
+// This event is used to signal the AU Server thread manager that
+// there are additional server threads needed.
+//
+
+
+HANDLE LsapAuThreadManagementEvent;
+
+
+
+//
+// Authentication API routine dispatch table
+//
+
+PLSAP_AU_API_DISPATCH LsapAuApiDispatch[LsapAuMaxApiNumber] = {
+ LsapAuApiDispatchLookupPackage,
+ LsapAuApiDispatchLogonUser,
+ LsapAuApiDispatchCallPackage,
+ LsapAuApiDeregisterLogonProcess
+ };
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Prototypes for routines private to this module //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+VOID
+LsapAuGetCountOfThreadsToCreate(
+ OUT PLONG ThreadsToCreate,
+ OUT PLONG ThreadIndex
+ );
+
+
+VOID
+LsapAuProvideWorkerThreads( VOID );
+
+
+VOID
+LsapAuCreateServerThreads( VOID );
+
+
+NTSTATUS
+LsapAuThreadManager (
+ IN PVOID ThreadParameter
+ );
+
+
+ULONG
+LsapAuInterlockedIncrement(
+ IN PULONG Value
+ );
+
+ULONG
+LsapAuInterlockedDecrement(
+ IN PULONG Value
+ );
+
+ULONG
+LsapAuInterlockedRead(
+ IN PULONG Value
+ );
+
+
+NTSTATUS
+LsapAuCreatePortSD(
+ PSECURITY_DESCRIPTOR * SecurityDescriptor
+ );
+
+
+
+
+NTSTATUS
+LsapAuHandleConnectionRequest(
+ IN PLSAP_AU_API_MESSAGE Message
+ )
+
+/*++
+
+Routine Description:
+
+ This loop waits for connection requests from logon processes.
+
+ When such a connection is received, the caller is validated as
+ being a logon process. A logon process is a process with the
+ SeTcbPrivilege. If the connect request message is all zeroes,
+ than an untrusted connection is created.
+
+Arguments:
+
+ ThreadParameter - Not used.
+
+Return Value:
+
+ Success - but this thread never exits.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_LOGON_PROCESS LogonProcessContext;
+ REMOTE_PORT_VIEW ClientView;
+ PLSAP_AU_REGISTER_CONNECT_INFO ConnectInfo;
+ BOOLEAN Accept;
+ HANDLE CommPort;
+
+ ConnectInfo = &Message->ConnectionRequest;
+
+ //
+ // Validate that the caller has the SeTcbPrivilege.
+ // If valid, a new logon process context is automatically
+ // created.
+ //
+ // Pass in ConnectInfo so we can extract the name of the
+ // logon process.
+ //
+
+ ConnectInfo->CompletionStatus = LsapValidLogonProcess(
+ &Message->PortMessage.ClientId,
+ ConnectInfo,
+ &LogonProcessContext
+ );
+
+
+ if ( ConnectInfo->CompletionStatus == STATUS_SUCCESS ) {
+ Accept = TRUE;
+#ifdef LSAP_AU_TRACK_CONTEXT
+DbgPrint("New: 0x%lx\n", LogonProcessContext);
+#endif //LSAP_AU_TRACK_CONTEXT
+ } else {
+ Accept = FALSE;
+ }
+
+
+
+ ClientView.Length = sizeof(ClientView);
+ Status = NtAcceptConnectPort(
+ &CommPort,
+ (PVOID *) LogonProcessContext,
+ (PPORT_MESSAGE) Message,
+ Accept,
+ NULL,
+ &ClientView
+ );
+#if DBG
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("LsaSrv: (Register) NtAcceptConnectPort failed: 0x%lx\n", Status);
+ }
+#endif //DBG
+ ASSERT( NT_SUCCESS(Status) );
+
+ if ( Accept ) {
+
+
+ LogonProcessContext->CommPort = CommPort;
+
+
+ //
+ // Add this context to our list of valid logon process contexts.
+ // This has to happen before the call to complete the port because
+ // calls may come in while the port is being completed.
+ //
+
+ LsapAuAddClientContext(LogonProcessContext);
+
+
+ //
+ // And complete the connection.
+ //
+
+ Status = NtCompleteConnectPort(CommPort);
+ ASSERT( NT_SUCCESS(Status) );
+
+ //
+ // We don't need to access the context any more, so
+ // dereference it.
+ //
+
+ LsapAuDereferenceClientContext( LogonProcessContext );
+
+ }
+
+ return Status;
+
+}
+
+
+NTSTATUS
+LsapAuServerLoop (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main listener loop for calls to the LSA from
+ logon processes. This routine dispatches LSA authentication
+ requests to their dispatch routines.
+
+Arguments:
+
+ ThreadParameter - Indicates how many active threads there currently
+ are.
+
+Return Value:
+
+ Success - but it never exits.
+
+--*/
+
+{
+ NTSTATUS
+ Status;
+
+ LSAP_AU_API_MESSAGE
+ Request;
+
+ PLSAP_AU_API_MESSAGE
+ Reply;
+
+ LSAP_CLIENT_REQUEST
+ ClientRequest;
+
+ LONG
+ ThreadIndex,
+ FreeCount,
+ ActiveCount;
+
+ BOOLEAN
+ ExitWhenDone = FALSE;
+
+ ULONG
+ KeepAroundForAWhile;
+
+ BOOLEAN
+ ContextFound,
+ InvalidateContext,
+ TrustedClient;
+
+
+#if LSAP_DIAGNOSTICS
+ LUID
+ ThreadLuid;
+
+ Status = NtAllocateLocallyUniqueId( &ThreadLuid );
+ ASSERT(NT_SUCCESS(Status));
+#endif //LSAP_DIAGNOSTICS
+
+
+ //
+ // Don't exit right away - handle bursts.
+ //
+
+ KeepAroundForAWhile = LsapAuCallsToProcess;
+
+
+ //
+ // Increment the active server thread count
+ //
+
+ ThreadIndex = (ULONG)(ThreadParameter);
+ if (ThreadIndex > LsapAuMinimumThreads) {
+ ExitWhenDone = TRUE;
+
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): Server thread (%lx, %lx) created and will exit when done.\n"
+ " ThreadIndex: 0x%lx\n",
+ ThreadLuid.HighPart, ThreadLuid.LowPart, ThreadIndex) );
+#if LSAP_DIAGNOSTICS
+ } else {
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): Server thread (%lx, %lx) created and will NOT exit when done.\n"
+ " ThreadIndex: 0x%lx\n",
+ ThreadLuid.HighPart, ThreadLuid.LowPart, ThreadIndex) );
+#endif //LSAP_DIAGNOSTICS
+ }
+
+
+ //
+ // Set the client call context to point to the request message buffer.
+ //
+
+ ClientRequest.Request = &Request;
+
+
+ //
+ // First time through, there is no reply.
+ //
+
+ Reply = NULL;
+
+
+ //
+ // Coming into this thread, the thread is marked as free.
+ // However, the loop below expects the thread to be busy,
+ // and so it marks it as free before waiting for another
+ // message.
+ //
+ // Mark our thread as busy to meet the initial condition
+ // requirements of the loop.
+ //
+
+ FreeCount = LsapAuInterlockedDecrement(&LsapAuFreeThreads);
+
+ //
+ // Now loop indefinitely, processing requests
+ //
+
+ for(;;) {
+
+
+ FreeCount = LsapAuInterlockedIncrement(&LsapAuFreeThreads);
+ Status = NtReplyWaitReceivePort(
+ LsapAuApiPort,
+ (PVOID *) &ClientRequest.LogonProcessContext,
+ (PPORT_MESSAGE) Reply,
+ (PPORT_MESSAGE) &Request
+ );
+
+#if LSAP_DIAGNOSTICS
+if (Request.PortMessage.u2.s2.Type != LPC_CONNECTION_REQUEST) {
+ if (Request.PortMessage.u2.s2.Type == LPC_PORT_CLOSED) {
+ LsapDiagPrint( AU_MESSAGES, ("** Port Closed **\n") );
+ LsapDiagPrint( AU_MESSAGES, (" Context = 0x%lx\n",
+ ClientRequest.LogonProcessContext) );
+ } else if (Request.PortMessage.u2.s2.Type == LPC_CLIENT_DIED) {
+ LsapDiagPrint( AU_MESSAGES, ("** Client Died **\n") );
+ LsapDiagPrint( AU_MESSAGES, (" Context = 0x%lx\n",
+ ClientRequest.LogonProcessContext) );
+ } else {
+ ASSERT(Request.PortMessage.u2.s2.Type == LPC_REQUEST);
+ LsapDiagPrint( AU_MESSAGES, (" Call: ") );
+ switch (Request.ApiNumber) {
+ case LsapAuLookupPackageApi:
+ LsapDiagPrint( AU_MESSAGES, ("Lookup Package\n") );
+ break;
+ case LsapAuLogonUserApi:
+ LsapDiagPrint( AU_MESSAGES, ("Logon User\n") );
+ break;
+ case LsapAuCallPackageApi:
+ LsapDiagPrint( AU_MESSAGES, ("Call Package\n") );
+ LsapDiagPrint( AU_MESSAGES, (" Function: TBD\n") );
+ break;
+ case LsapAuDeregisterLogonProcessApi:
+ LsapDiagPrint( AU_MESSAGES, ("Deregister\n") );
+ break;
+ default:
+ LsapDiagPrint( AU_MESSAGES, ("Unknown (%d)\n",
+ Request.ApiNumber) );
+ break;
+ } //end switch
+ LsapDiagPrint( AU_MESSAGES, (" Context = 0x%lx\n",
+ ClientRequest.LogonProcessContext) );
+ }
+}
+#endif //LSAP_DIAGNOSTICS
+
+
+ FreeCount = LsapAuInterlockedDecrement(&LsapAuFreeThreads);
+ if (ExitWhenDone == TRUE) {
+ KeepAroundForAWhile --;
+ }
+
+
+
+
+
+ //
+ // It is highly unusual, but not completely impossible,
+ // that a logon process will evaporate from underneath
+ // us while we are processing an LPC call.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status == STATUS_INVALID_CID) {
+ //
+ // See if we can find the client's context and delete it.
+ //
+
+ ContextFound = LsapAuReferenceClientContext(
+ &ClientRequest,
+ TRUE, // Remove context
+ &TrustedClient );
+
+ if (ContextFound) {
+
+ //
+ // Dereferencing the context will cause it to be rundown.
+ //
+
+ LsapAuDereferenceClientContext(ClientRequest.LogonProcessContext);
+ }
+
+ } else {
+
+ //
+ // Unusual - we got an unexpected error.
+ // Continue as best we can.
+ //
+
+#if DBG
+ DbgPrint("\nLsa (au): Unexpected error on NtReplyWaitRecievePort()\n Status = 0x%lx\n", Status);
+#endif //DBG
+ }
+
+ //
+ // Do another wait, this time with no reply.
+ //
+
+ Reply = NULL;
+
+ continue;
+ }
+
+
+ //
+ // Ensure there are enough server worker threads.
+ //
+
+ LsapAuProvideWorkerThreads();
+
+
+
+
+ //
+ // Dispatch the call . . .
+ //
+
+ if (Request.PortMessage.u2.s2.Type == LPC_CONNECTION_REQUEST) {
+ Status = LsapAuHandleConnectionRequest( &Request );
+ Reply = NULL;
+
+ } else if (
+ (Request.PortMessage.u2.s2.Type == LPC_PORT_CLOSED) ||
+ (Request.PortMessage.u2.s2.Type == LPC_CLIENT_DIED)
+
+ ) {
+
+ //
+ // These messages could be received in any of the following
+ // conditions:
+ //
+ // 1) A bug in the client side has inadvertantly closed
+ // the port handle.
+ //
+ // 2) The LsaDeregisterLogonProcess() call has called
+ // lsa, recevied completion status, and then beat
+ // lsa in closing the port (a race condition that
+ // can not be eliminated).
+ //
+ // 3) The client has died and the comm port is being
+ // rundown as part of process rundown.
+ //
+ // The first case is a bug, and the client is bound to find
+ // out about it real soon. In the second case, we will normally
+ // not have a client context left to reference, so looking for it
+ // won't hurt anything. So, the correct behaviour here is to
+ // try to reference and then delete the client's context.
+
+ ContextFound = LsapAuReferenceClientContext(
+ &ClientRequest,
+ TRUE, // Remove context
+ &TrustedClient );
+
+ if (ContextFound) {
+
+ //
+ // Dereferencing the context will cause it to be rundown.
+ //
+
+ LsapAuDereferenceClientContext(ClientRequest.LogonProcessContext);
+ }
+
+ //
+ // In any of these cases, there is nobody to reply to
+ //
+
+ Reply = NULL;
+
+
+ } else if (Request.ApiNumber >= LsapAuMaxApiNumber ) {
+
+ //
+ // This is an error in the client
+ //
+
+#if DBG
+ DbgPrint( "LSA AU: Invalid Api Number (%lx)\n",
+ Request.ApiNumber
+ );
+
+#endif
+
+ Reply = &Request;
+ Reply->ReturnedStatus = STATUS_INVALID_SYSTEM_SERVICE;
+
+ } else if (Request.PortMessage.u2.s2.Type == LPC_REQUEST) {
+
+ //
+ // If this is a Deregister call, then we want to invalidate
+ // the client's context when we reference it. Otherwise,
+ // leave the context valid.
+ //
+
+ if (Request.ApiNumber == LsapAuDeregisterLogonProcessApi) {
+ InvalidateContext = TRUE;
+ } else {
+ InvalidateContext = FALSE;
+ }
+
+ //
+ // Try to refrence the context. If one isn't found,
+ // then we must be deleting it in another thread.
+ //
+
+ ContextFound = LsapAuReferenceClientContext(
+ &ClientRequest,
+ InvalidateContext,
+ &TrustedClient);
+
+ if (ContextFound) {
+
+ //
+ // If the request is to deregister, send a reply and
+ // then dereference the context. This will cause it
+ // to be deleted since we already invalidated it when
+ // we referenced it.
+ //
+
+ if (Request.ApiNumber == LsapAuDeregisterLogonProcessApi) {
+ Reply = &Request;
+ Reply->ReturnedStatus = STATUS_SUCCESS;
+ Status = NtReplyPort(
+ LsapAuApiPort,
+ (PPORT_MESSAGE) Reply
+ );
+
+ //
+ // Make sure when we do the next wait, we don't
+ // send another reply.
+ //
+
+ Reply = NULL;
+
+ //
+ // No api to dispatch to, the dereference of the
+ // context will do all the necessary work.
+ //
+
+ } else {
+
+
+ //
+ // Valid API number other than deregister - dispatch it
+ //
+
+ Status = (LsapAuApiDispatch[Request.ApiNumber])(
+ &ClientRequest,
+ TrustedClient
+ );
+
+
+ Reply = &Request;
+ Reply->ReturnedStatus = Status;
+
+
+
+ }
+
+
+ //
+ // Whatever the API number, we now need to dereference
+ // the client context we referenced. Note that in the
+ // case of a Deregister call, this will cause the context
+ // to be rundown.
+ //
+
+ LsapAuDereferenceClientContext(
+ ClientRequest.LogonProcessContext);
+ }
+
+
+
+
+ } else {
+
+ //
+ // This is a totally unexpected situation, but we will
+ // cover our posterier just in case we come across an
+ // unexpected error.
+ //
+
+ Reply = NULL;
+
+ } // end_if
+
+
+ //
+ // There are a number of conditions which require us to
+ // send a response before we can again wait for another
+ // request. These are:
+ //
+ // 1) We are a temporary worker thread and it is time for
+ // us to exit.
+ //
+ // 2) We need to create another worker thread. We can't do
+ // this while processing an LPC call because we will cause
+ // a call to be made to csrss (for "new thread notification")
+ // and this might result in a deadlock.
+ //
+
+
+ if (ExitWhenDone && (KeepAroundForAWhile == 0)) {
+
+ //
+ // Send a reply if one is required
+ //
+
+ if ( Reply != NULL ) {
+
+
+ Status = NtReplyPort(
+ LsapAuApiPort,
+ (PPORT_MESSAGE) Reply
+ );
+ //
+ // Don't send a reply when we do the next wait
+ //
+
+ Reply = NULL;
+
+#if DBG
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Lsa\\server\\auloop.c: Reply to client failed.\n");
+ DbgPrint(" Status: 0x%lx\n", Status);
+ }
+#endif //DBG
+ }
+ }
+
+
+ //
+ // If it is time for this thread to exit, then do that now.
+ //
+
+ if (ExitWhenDone && KeepAroundForAWhile == 0) {
+ ActiveCount = LsapAuInterlockedDecrement(&LsapAuActiveThreads);
+
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): Temporary server thread (%lx, %lx) exiting.\n"
+ " Active count decremented to: 0x%lx\n",
+ ThreadLuid.HighPart, ThreadLuid.LowPart,
+ ActiveCount) );
+#if DBG
+ if (ActiveCount == 0) {
+ DbgPrint("Lsa\\server\\auloop.c: Last active server thread exiting.\n");
+ DbgPrint(" This is bad. Nobody can logon.\n");
+ }
+#endif //DBG
+
+ return(STATUS_SUCCESS);
+ }
+
+ } // end_for
+
+
+ return STATUS_SUCCESS;
+}
+
+
+BOOLEAN
+LsapAuLoopInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a port for communicating with logon processes.
+ It then creates threads to listen for logon process connections
+ and to act upon calls from those logon processes.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES PortObjectAttributes;
+ STRING PortName;
+ UNICODE_STRING UnicodePortName;
+ DWORD Ignore;
+ HANDLE Thread;
+ PSECURITY_DESCRIPTOR PortSD = NULL;
+
+ Status = LsapAuCreatePortSD(
+ &PortSD
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(FALSE);
+ }
+
+ //
+ // Create the LPC port
+ //
+
+ RtlInitString(&PortName,"\\LsaAuthenticationPort");
+ Status = RtlAnsiStringToUnicodeString( &UnicodePortName, &PortName, TRUE );
+ ASSERT( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &PortObjectAttributes,
+ &UnicodePortName,
+ 0,
+ NULL,
+ PortSD
+ );
+
+ Status = NtCreatePort(
+ &LsapAuApiPort,
+ &PortObjectAttributes,
+ sizeof(LSAP_AU_REGISTER_CONNECT_INFO),
+ sizeof(LSAP_AU_API_MESSAGE),
+ sizeof(LSAP_AU_API_MESSAGE) * 32
+ );
+ RtlFreeUnicodeString( &UnicodePortName );
+ ASSERT( NT_SUCCESS(Status) );
+
+ //
+ // Create the Thread Management thread
+ //
+
+ Status = NtCreateEvent(
+ &LsapAuThreadManagementEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): Thread management event created. (0x%lx)\n",
+ Status));
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Create the thread management thread
+ //
+
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE)LsapAuThreadManager,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+
+
+
+ //
+ // Set up the dynamic thread controls based upon our product type.
+ // Workstations have lower limits than servers.
+ //
+
+ RtlInitializeResource(&LsapAuThreadCountLock);
+
+ LsapAuActiveThreads = 1; // 1 assumes we will create the
+ LsapAuFreeThreads = 1; // initial thread below.
+
+ if (LsapProductType == NtProductWinNt) {
+
+ LsapAuFreeThreadsGoal = 3;
+ LsapAuMinimumThreads = 3;
+ LsapAuMaximumThreads = 20;
+ LsapAuCallsToProcess = 8;
+
+ } else {
+
+ //
+ // Server values
+ //
+
+ LsapAuFreeThreadsGoal = 6;
+ LsapAuMinimumThreads = 6;
+ LsapAuMaximumThreads = 24;
+ LsapAuCallsToProcess = 20;
+ }
+
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): Thread tracking values -\n"
+ " LsapAuActiveThreads: %ld\n"
+ " LsapAuFreeThreads: %ld\n"
+ " LsapAuFreeThreadsGoal: %ld\n"
+ " LsapAuMinimumThreads: %ld\n"
+ " LsapAuMaximumThreads: %ld\n"
+ " LsapAuCallsToProcess: %ld\n",
+ LsapAuActiveThreads, LsapAuFreeThreads,
+ LsapAuFreeThreadsGoal, LsapAuMinimumThreads,
+ LsapAuMaximumThreads, LsapAuCallsToProcess ) );
+
+
+ //
+ // Create a thread to process connects and requests to our AuApiPort
+ //
+
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE)LsapAuServerLoop,
+ 0L, // Initial thread has an index of 0
+ 0L,
+ &Ignore
+ );
+
+#if DBG
+ if (Thread == NULL) {
+ DbgPrint("\nLSASS: Couldn't Start Thread To Service Logon Process Calls.\n");
+ DbgPrint(" This is bad and will prevent logon to all accounts\n");
+ DbgPrint(" DEVL: except the SYSTEM account\n");
+ DbgPrint(" Status is: %dl (0x%lx)\n\n", GetLastError(),GetLastError() );
+ }
+#endif //DBG
+
+
+
+
+ return TRUE;
+
+}
+
+
+VOID
+LsapAuGetCountOfThreadsToCreate(
+ OUT PLONG ThreadsToCreate,
+ OUT PLONG ThreadIndex
+ )
+/*++
+
+Routine Description:
+
+ This function determines how many threads need to be created
+ to ensure there are 'LsapAuFreeThreadsGoal' free threads.
+
+ Overflows are not checked for.
+
+
+Arguments:
+
+ ThreadsToCreate - Receives the number of threads that should
+ be created.
+
+ ThreadIndex - Indicates how many threads are already active.
+ This value should be incremented for each thread created.
+ This value is used by the created threads to determine
+ whether they are permanent threads (index <= LsapAuMinimumThreads)
+ or temporary threads (index > LsapAuMinumumThreads). This
+ value is only returned if ThreadsToCreate is greater than
+ zero.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONG
+ CreateCount;
+
+ //
+ // Acquire the interlock
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
+
+ if (LsapAuFreeThreads < LsapAuFreeThreadsGoal) {
+ CreateCount = LsapAuFreeThreadsGoal - LsapAuFreeThreads;
+ LsapAuFreeThreads = LsapAuFreeThreadsGoal;
+ (*ThreadIndex) = LsapAuActiveThreads;
+ LsapAuActiveThreads += CreateCount;
+ } else {
+ CreateCount = 0;
+ }
+
+ (*ThreadsToCreate) = CreateCount;
+
+
+ //
+ // Release the interlock and return
+ //
+
+ (VOID)RtlReleaseResource( &LsapAuThreadCountLock );
+
+ return;
+}
+
+
+VOID
+LsapAuProvideWorkerThreads( VOID )
+
+/*++
+
+Routine Description:
+
+ This function determines whether or not any worker threads need
+ to be created to ensure there are 'LsapAuFreeThreadsGoal' free
+ threads.
+
+ If any need to be created, then an asynchronous request is made
+ of our thread management thread to create the additional threads.
+
+
+
+Arguments:
+
+ None - global variables are used.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ LONG
+ Free,
+ FreeGoal,
+ Active,
+ MaximumThreads;
+
+
+ //
+ // Acquire the interlock
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
+
+
+ Free = LsapAuFreeThreads;
+ FreeGoal = LsapAuFreeThreadsGoal;
+ Active = LsapAuActiveThreads;
+ MaximumThreads = LsapAuMaximumThreads;
+
+ //
+ // Release the interlock
+ //
+
+ (VOID)RtlReleaseResource( &LsapAuThreadCountLock );
+
+ if ( (Active < MaximumThreads ) &&
+ (Free < FreeGoal) ) {
+
+ //
+ // Need to create additional threads
+ //
+
+ LsapAuCreateServerThreads();
+ }
+
+ return;
+}
+
+
+
+VOID
+LsapAuCreateServerThreads( VOID )
+
+/*++
+
+Routine Description:
+
+ Signal an event that will cause our ThreadManagement thread to
+ create more threads if necessary.
+
+
+Arguments:
+
+ None - global variables are used.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS
+ NtStatus;
+
+ NtStatus = NtSetEvent( LsapAuThreadManagementEvent, NULL );
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): Signalled ThreadManagement event (0x%lx)\n",
+ NtStatus));
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ return;
+}
+
+
+
+NTSTATUS
+LsapAuThreadManager (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+ This thread is used to create AU worker threads when
+ needed. It is signaled via an event. When signalled,
+ it checks to see if any worker threads need to be created
+ and, if so, creates them.
+
+ Worker thread creation use to be done in the main AU loop
+ just after receiving an LPC message. This ended up causing a
+ deadlock if CSR was making a remote file access.
+
+
+Arguments:
+
+ ThreadParameter - Not used.
+
+
+Return Value:
+
+ This thread never exits.
+
+--*/
+
+{
+ NTSTATUS
+ Status,
+ Ignore;
+
+ ULONG
+ ThreadsToCreate,
+ ThreadIndex,
+ i;
+
+ HANDLE
+ Thread;
+
+
+ //
+ // Loop forever waiting to be given the opportunity to
+ // serve the greater good.
+ //
+
+ for (; ; ) {
+
+ //
+ // Wait to be notified that there is work to be done
+ //
+
+ Status =
+ NtWaitForSingleObject( LsapAuThreadManagementEvent, TRUE, NULL);
+
+ //
+ // Now create any threads, if necessary
+ //
+
+ LsapAuGetCountOfThreadsToCreate( &ThreadsToCreate, &ThreadIndex );
+
+#if LSAP_DIAGNOSTICS
+ if (ThreadsToCreate > 0) {
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): creating %ld new server threads.\n",
+ ThreadsToCreate) );
+ }
+#endif //LSAP_DIAGNOSTICS
+
+ for (i=0; i < ThreadsToCreate; i++) {
+ LsapDiagPrint( AU_TRACK_THREADS,
+ ("Lsa (au): Server thread %ld created.\n", i) );
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE)LsapAuServerLoop,
+ (LPVOID)ThreadIndex++,
+ 0L,
+ &Ignore
+ );
+#if DBG
+ if (Thread == NULL) {
+ DbgPrint("\nLSASS: Couldn't Start auxiliary Thread To Service Logon Process Calls.\n");
+ DbgPrint(" This is non fatal and usually indicates a resource\n");
+ DbgPrint(" has been depleted.\n");
+ DbgPrint(" Thread number: %dl\n", LsapAuActiveThreads);
+ DbgPrint(" Status is: %d (0x%lx)\n\n", GetLastError(),GetLastError() );
+ }
+#endif //DBG
+ (VOID) CloseHandle( Thread );
+ }
+
+ }
+
+
+ return STATUS_SUCCESS;
+}
+
+
+
+
+ULONG
+LsapAuInterlockedIncrement(
+ IN PULONG Value
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs in interlocked increment of the provided
+ value, returning the resultant value.
+
+ Overflows are not checked for.
+
+
+Arguments:
+
+ Value - Address of the value to increment.
+
+Return Value:
+
+ The resultant value (following the increment).
+
+--*/
+
+{
+ ULONG ReturnValue;
+
+ //
+ // Acquire the interlock
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
+
+
+ //
+ // Increment the value and save the resultant value.
+ //
+
+ (*Value) += 1;
+ ReturnValue = (*Value);
+
+
+ //
+ // Release the interlock and return
+ //
+
+ (VOID)RtlReleaseResource( &LsapAuThreadCountLock );
+
+ return(ReturnValue);
+
+
+}
+
+
+ULONG
+LsapAuInterlockedDecrement(
+ IN PULONG Value
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs in interlocked decrement of the provided
+ value, returning the resultant value.
+
+ Overflows are not checked for.
+
+
+Arguments:
+
+ Value - Address of the value to decrement.
+
+Return Value:
+
+ The resultant value (following the decrement).
+
+--*/
+
+{
+ ULONG ReturnValue;
+
+ //
+ // Acquire the interlock
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
+
+
+ //
+ // Decrement the value and save the resultant value.
+ //
+
+ (*Value) -= 1;
+ ReturnValue = (*Value);
+
+
+ //
+ // Release the interlock and return
+ //
+
+ (VOID)RtlReleaseResource( &LsapAuThreadCountLock );
+
+ return(ReturnValue);
+
+
+}
+
+
+ULONG
+LsapAuInterlockedRead(
+ IN PULONG Value
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs in interlocked read of the provided
+ value.
+
+
+
+Arguments:
+
+ Value - Address of the value to read.
+
+Return Value:
+
+ The read value.
+
+--*/
+
+{
+ ULONG ReturnValue;
+
+ //
+ // Acquire the interlock
+ //
+
+ (VOID)RtlAcquireResourceExclusive( &LsapAuThreadCountLock, TRUE );
+
+
+ //
+ // Decrement the value and save the resultant value.
+ //
+
+ ReturnValue = (*Value);
+
+
+ //
+ // Release the interlock and return
+ //
+
+ (VOID)RtlReleaseResource( &LsapAuThreadCountLock );
+
+ return(ReturnValue);
+
+
+}
+
+
+
+NTSTATUS
+LsapAuCreatePortSD(
+ PSECURITY_DESCRIPTOR * SecurityDescriptor
+ )
+/*++
+
+Routine Description:
+
+ This function creates a security descriptor for the LSA LPC port. It
+ grants World PORT_CONNECT access and local system GENERIC_ALL and
+ Administrators GENERIC_READ, GENERIC_EXECUTE, and READ_CONTROL access.
+
+Arguments:
+
+ SecurityDescriptor - Receives a pointer to the new security descriptor.
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+ NTSTATUS
+ Status;
+
+ ULONG
+ AclLength;
+
+ PACL
+ PortDacl = NULL;
+
+
+ //
+ // Set up a default ACLs
+ //
+ // Public: WORLD:execute, SYSTEM:all, ADMINS:(read|execute|read_control)
+ // System: SYSTEM:all, ADMINS:(read|execute|read_control)
+
+ AclLength = (ULONG)sizeof(ACL) +
+ (3*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
+ RtlLengthSid( LsapLocalSystemSid ) +
+ RtlLengthSid( LsapAliasAdminsSid ) +
+ RtlLengthSid( LsapWorldSid );
+
+
+ PortDacl = (PACL) LsapAllocateLsaHeap( AclLength );
+ if (PortDacl == NULL) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ *SecurityDescriptor = (PSECURITY_DESCRIPTOR)
+ LsapAllocateLsaHeap( sizeof(SECURITY_DESCRIPTOR) );
+
+ if (*SecurityDescriptor == NULL) {
+ LsapFreeLsaHeap( PortDacl );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+
+ Status = RtlCreateAcl( PortDacl, AclLength, ACL_REVISION2);
+
+ //
+ // WORLD access
+ //
+
+ Status = RtlAddAccessAllowedAce (
+ PortDacl,
+ ACL_REVISION2,
+ PORT_CONNECT,
+ LsapWorldSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // SYSTEM access
+
+ //
+
+ Status = RtlAddAccessAllowedAce (
+ PortDacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ LsapLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // ADMINISTRATORS access
+ //
+
+ Status = RtlAddAccessAllowedAce (
+ PortDacl,
+ ACL_REVISION2,
+ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL,
+ LsapAliasAdminsSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+
+ //
+ // Now initialize security descriptors
+ // that export this protection
+ //
+
+
+
+ Status = RtlCreateSecurityDescriptor(
+ *SecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION1
+ );
+ ASSERT( NT_SUCCESS(Status) );
+ Status = RtlSetDaclSecurityDescriptor(
+ *SecurityDescriptor,
+ TRUE, // DaclPresent
+ PortDacl,
+ FALSE // DaclDefaulted
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ return( STATUS_SUCCESS );
+}
diff --git a/private/lsa/server/aumsp.c b/private/lsa/server/aumsp.c
new file mode 100644
index 000000000..1e81c9ea4
--- /dev/null
+++ b/private/lsa/server/aumsp.c
@@ -0,0 +1,141 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ aumsp.c
+
+Abstract:
+
+ This module provides private LSA services available to Microsoft
+ authentication packages.
+
+Author:
+
+ Jim Kelly (JimK) 3-May-1991
+
+Revision History:
+
+--*/
+
+#include "ausrvp.h"
+#include <string.h>
+
+
+NTSTATUS
+LsapAuImpersonateClient (
+ PLSA_CLIENT_REQUEST ClientRequest
+ );
+
+
+
+BOOLEAN
+LsapAuMspInitialize()
+
+/*++
+
+Routine Description:
+
+ This function initializes data structures related to the private
+ services provided to Microsoft authentication packages.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE - Initialization completed successfully.
+
+ FALSE - Initialization failed.
+
+--*/
+
+{
+
+
+ //
+ // Initialize the table of private LSA routines provided to Microsoft
+ // authentication packages.
+ //
+
+
+ LsapPrivateLsaApi.GetOperationalMode = NULL; //FIX, FIX
+ LsapPrivateLsaApi.ImpersonateClient = &LsapAuImpersonateClient;
+
+ return TRUE;
+
+}
+
+NTSTATUS
+LsapAuImpersonateClient (
+ PLSA_CLIENT_REQUEST ClientRequest
+ )
+
+/*++
+
+Routine Description:
+
+ Called by Microsoft authentication packages to impersonate
+ LSA's client
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+ //
+ // Cast the opaque structure
+ //
+
+ PLSAP_CLIENT_REQUEST IClientRequest = (LSAP_CLIENT_REQUEST *)ClientRequest;
+
+ return NtImpersonateClientOfPort( IClientRequest->LogonProcessContext->CommPort,
+ &IClientRequest->Request->PortMessage
+ );
+}
+
+
+
+NTSTATUS
+LsapAuGetOperationalMode(
+ OUT PLSA_OPERATIONAL_MODE OperationalMode
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the operational mode of the system.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - The service has completed successfully.
+
+--*/
+
+{
+
+ //FIX, FIX - get THIS from configuration control database when available.
+
+ (*OperationalMode) = (LSA_MODE_PASSWORD_PROTECTED |
+ LSA_MODE_INDIVIDUAL_ACCOUNTS);
+
+ return STATUS_SUCCESS;
+
+}
+
diff --git a/private/lsa/server/aupckg.c b/private/lsa/server/aupckg.c
new file mode 100644
index 000000000..f8b8388ad
--- /dev/null
+++ b/private/lsa/server/aupckg.c
@@ -0,0 +1,792 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ aupckg.c
+
+Abstract:
+
+ This module provides code that initializes authentication packages.
+
+ It also provides the dispatch code for LsaLookupPackage() and
+ LsaCallPackage().
+
+Author:
+
+ Jim Kelly (JimK) 27-February-1991
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "ausrvp.h"
+
+
+
+BOOLEAN
+LsapPackageInitialize()
+
+/*++
+
+Routine Description:
+
+ This function initializes data structures used to track authentication
+ packages and then makes a call to initialize each authentication package
+ we are configured to run with.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOLEAN ReturnStatus;
+
+ //
+ // Initialize the private LSA services available to Microsoft
+ // authentication packages.
+ //
+
+ ReturnStatus = LsapAuMspInitialize();
+
+
+ //
+ // Initialize the dispatch table provided to authentication
+ // packages.
+ //
+
+
+ LsapPackageDispatchTable.CreateLogonSession = &LsapCreateLogonSession;
+ LsapPackageDispatchTable.DeleteLogonSession = &LsapDeleteLogonSession;
+ LsapPackageDispatchTable.AddCredential = &LsapAddCredential;
+ LsapPackageDispatchTable.GetCredentials = &LsapGetCredentials;
+ LsapPackageDispatchTable.DeleteCredential = &LsapDeleteCredential;
+ LsapPackageDispatchTable.AllocateLsaHeap = &LsapAllocateLsaHeap;
+ LsapPackageDispatchTable.FreeLsaHeap = &LsapFreeLsaHeap;
+ LsapPackageDispatchTable.AllocateClientBuffer = &LsapAllocateClientBuffer;
+ LsapPackageDispatchTable.FreeClientBuffer = &LsapFreeClientBuffer;
+ LsapPackageDispatchTable.CopyToClientBuffer = &LsapCopyToClientBuffer;
+ LsapPackageDispatchTable.CopyFromClientBuffer = &LsapCopyFromClientBuffer;
+
+
+ //
+ // Authentication packages are identified by a ULONG value.
+ // Each one is tracked in an array of authentication packages.
+ //
+
+ LsapPackageCount = 0;
+ LsapPackageArray = (PLSAP_PACKAGE_ARRAY)NULL;
+
+
+ //
+ // Load each configured authentication package
+ //
+
+ LsapConfigurePackages();
+
+
+
+ return TRUE;
+
+}
+
+
+
+NTSTATUS
+LsapConfigurePackage(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+RTL_QUERY_REGISTRY_TABLE LsapRegistryConfigurationTable[] = {
+
+ {LsapConfigurePackage, 0,
+ L"Authentication Packages",NULL,
+ REG_NONE, NULL, 0},
+
+ {NULL, 0,
+ NULL, NULL,
+ REG_NONE, NULL, 0}
+
+};
+
+
+NTSTATUS
+LsapConfigurePackages()
+
+/*++
+
+Routine Description:
+
+ This routine retrieves configuration information regarding
+ which authentication packages are to be loaded and then
+ loads each package.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - Any errors are logged, but nothing happens to cause
+ initialization to fail.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+
+ Status = RtlQueryRegistryValues( RTL_REGISTRY_CONTROL,
+ L"Lsa",
+ LsapRegistryConfigurationTable,
+ NULL,
+ NULL
+ );
+#if DEVL
+ if ( !NT_SUCCESS(Status) ) {
+ DbgPrint("LSA: Warning. Unable to read registry data - Status == %lx\n", Status);
+ }
+#endif // DEVL
+
+ return Status;
+}
+
+NTSTATUS
+LsapConfigurePackage(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ UNICODE_STRING PackageName;
+
+ if (ValueType != REG_SZ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ PackageName.Buffer = ValueData;
+ PackageName.Length = (USHORT)(ValueLength - sizeof( UNICODE_NULL ));
+ PackageName.MaximumLength = (USHORT)ValueLength;
+ return LsapAddPackage( &PackageName,
+ NULL, // Not yet supported
+ NULL // Not yet supported
+ );
+}
+
+
+
+
+NTSTATUS
+LsapAddPackage(
+ IN PUNICODE_STRING PackageFileName,
+ IN PUNICODE_STRING DatabaseParameter,
+ IN PUNICODE_STRING ConfidentialityParameter
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes an authentication package that we are
+ configured to run with. Each package is assigned a unique ID and
+ added to the list of authentication packages.
+
+ Note that authentication packages are never expected to exit.
+
+
+Arguments:
+
+ PackageFileName - The name of the file that the authentication pacakge
+ DLL is in.
+
+ DatabaseParameter - The database source parameter value to pass to the
+ DLL. This enables the authentication package to locate its
+ package-specific database. The meaning and format of this string
+ are package-specific and opaque to the LSA.
+
+ ConfidentialityParameter - The confidentiality parameter to pass to the
+ DLL. This enables the authentication package to decrypt any
+ encrypted information in the authentication-package-specific
+ database. The meaning and format of this string are package-specific
+ and opaque to the LSA.
+
+Return Value:
+
+ STATUS_SUCCESS - The package has been successfully added.
+
+ STATUS_QUOTA_EXCEEDED - An attempt to allocate heap space has failed.
+ The most probable cause is memory quota has been exceeded.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_PACKAGE_CONTEXT NewPackage;
+ PLSAP_PACKAGE_ARRAY NewPackageArray;
+ ULONG i, NewArraySize, CurrentPackageCount;
+
+
+
+ //
+ // Allocate a new package context record.
+ //
+
+ NewPackage = (PLSAP_PACKAGE_CONTEXT)
+ LsapAllocateLsaHeap(
+ (ULONG)sizeof(LSAP_PACKAGE_CONTEXT)
+ );
+ if ( NewPackage == NULL ) {
+ return STATUS_QUOTA_EXCEEDED;
+ }
+
+
+
+ //
+ // Most of the rest of this has to be done with exclusive access
+ // to the package count or the package array.
+ //
+
+ LsapAuLock();
+
+
+ //
+ // Extend the package array
+ //
+
+ CurrentPackageCount = LsapPackageCount;
+ LsapPackageCount += 1;
+
+ NewArraySize = (ULONG)sizeof(PVOID) * LsapPackageCount;
+ NewPackageArray = LsapAllocateLsaHeap( NewArraySize );
+ if ( NewPackageArray == NULL ) {
+ LsapPackageCount -= 1;
+ LsapAuUnlock();
+ LsapFreeLsaHeap( NewPackage );
+ return STATUS_QUOTA_EXCEEDED;
+ }
+
+ //
+ // Copy the old array into the new one.
+ //
+
+ for (i=0; i<CurrentPackageCount ; i++ ) {
+ NewPackageArray->Package[i] = LsapPackageArray->Package[i];
+ }
+
+
+ //
+ // Set the last array address
+ //
+
+ NewPackageArray->Package[LsapPackageCount-1] = NewPackage;
+
+ //
+ // Load the authentication package
+ //
+
+ Status = LsapLoadPackage( PackageFileName, NewPackage );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ LsapPackageCount -= 1;
+ LsapAuUnlock();
+ LsapFreeLsaHeap( NewPackage );
+ LsapFreeLsaHeap( NewPackageArray );
+ return Status;
+ }
+
+ //
+ // Initialize the authentication package
+ //
+
+ Status = (NewPackage->PackageApi.LsapApInitializePackage) (
+ CurrentPackageCount,
+ &LsapPackageDispatchTable,
+ (PSTRING)DatabaseParameter,
+ (PSTRING)ConfidentialityParameter,
+ &NewPackage->Name
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ LsapPackageCount -= 1;
+ LsapAuUnlock();
+ DbgPrint("LSA: %wZ:Package failed to initialize\n", PackageFileName );
+ LsapFreeLsaHeap( NewPackage );
+ LsapFreeLsaHeap( NewPackageArray );
+ LsapUnloadPackage();
+ return Status;
+ }
+
+
+ //
+ // The package has been successfully loaded and initialized.
+ // Switch to the new package array.
+ //
+
+ if ( LsapPackageArray != NULL ) {
+ LsapFreeLsaHeap( LsapPackageArray );
+ }
+ LsapPackageArray = NewPackageArray;
+
+
+ LsapAuUnlock();
+
+ return STATUS_SUCCESS;
+
+
+}
+
+
+NTSTATUS
+LsapLoadPackage(
+ IN PUNICODE_STRING PackageFileName,
+ IN PLSAP_PACKAGE_CONTEXT NewPackage
+ )
+
+/*++
+
+Routine Description:
+
+ This function loads an authentication package dll.
+
+ This function must be called with exclusive access to shared
+ authentication data (LsapAuLock() called).
+
+Arguments:
+
+ PackageFileName - Name of the file that the authentication package
+ DLL resides in.
+
+ NewPackage - Pointer to the context record representing this new
+ authentication package. The dispatch table of this record will
+ be filled in upon successful loading of the authentication package.
+
+Return Value:
+
+ STATUS_SUCCESS - The package has been successfully loaded.
+
+
+ Anything returned from LdrLoadDll() or LdrGetProcedureAddress().
+
+--*/
+
+{
+
+
+ NTSTATUS Status, IgnoreStatus, MsProcStatus;
+ PVOID ModuleHandle;
+ STRING ProcedureName;
+ PLSA_AP_MS_INITIALIZE MsInitialize;
+ NTSTATUS TmpStatus;
+
+#if DBG
+ DbgPrint("LSA: Loading Authentication Package - %wZ\n", PackageFileName );
+#endif //DBG
+
+ Status = LdrLoadDll(
+ NULL,
+ NULL,
+ PackageFileName,
+ &ModuleHandle
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ DbgPrint("LSA: Failed to load Authentication Package.\n" );
+ DbgPrint(" Status = 0x%lx\n");
+ return Status;
+ }
+
+ LsapAdtAuditPackageLoad( PackageFileName );
+
+ //
+ // Now get the address of each dispatch routine
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ RtlInitString( &ProcedureName, LSA_AP_NAME_INITIALIZE_PACKAGE );
+ Status = LdrGetProcedureAddress(
+ ModuleHandle,
+ &ProcedureName,
+ 0,
+ (PVOID *)&NewPackage->PackageApi.LsapApInitializePackage
+ );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ RtlInitString( &ProcedureName, LSA_AP_NAME_CALL_PACKAGE );
+ Status = LdrGetProcedureAddress(
+ ModuleHandle,
+ &ProcedureName,
+ 0,
+ (PVOID *)&NewPackage->PackageApi.LsapApCallPackage
+ );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ RtlInitString( &ProcedureName, LSA_AP_NAME_LOGON_TERMINATED );
+ Status = LdrGetProcedureAddress(
+ ModuleHandle,
+ &ProcedureName,
+ 0,
+ (PVOID *)&NewPackage->PackageApi.LsapApLogonTerminated
+ );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ RtlInitString( &ProcedureName, LSA_AP_NAME_CALL_PACKAGE_UNTRUSTED );
+ IgnoreStatus = LdrGetProcedureAddress(
+ ModuleHandle,
+ &ProcedureName,
+ 0,
+ (PVOID *)&NewPackage->PackageApi.LsapApCallPackageUntrusted
+ );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ RtlInitString( &ProcedureName, LSA_AP_NAME_LOGON_USER );
+ TmpStatus = LdrGetProcedureAddress(
+ ModuleHandle,
+ &ProcedureName,
+ 0,
+ (PVOID *)&NewPackage->PackageApi.LsapApLogonUser
+ );
+
+ if (!NT_SUCCESS( TmpStatus )) {
+ NewPackage->PackageApi.LsapApLogonUser = NULL;
+ }
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // This procedure may or may not exist.
+ //
+
+ RtlInitString( &ProcedureName, LSA_AP_NAME_LOGON_USER_EX );
+ TmpStatus = LdrGetProcedureAddress(
+ ModuleHandle,
+ &ProcedureName,
+ 0,
+ (PVOID *)&NewPackage->PackageApi.LsapApLogonUserEx
+ );
+
+ if ( !NT_SUCCESS( TmpStatus ) ) {
+ NewPackage->PackageApi.LsapApLogonUserEx = NULL;
+ }
+ }
+
+ if (NT_SUCCESS( Status )) {
+
+ if (NewPackage->PackageApi.LsapApLogonUser == NULL && NewPackage->PackageApi.LsapApLogonUserEx == NULL) {
+ Status = TmpStatus;
+ }
+ }
+
+ //
+ // Microsoft authentication packages have one extra procedure, which
+ // will get called if available.
+ //
+ if ( NT_SUCCESS( Status ) ) {
+
+ RtlInitString( &ProcedureName, LSAP_AP_NAME_MS_INITIALIZE );
+ MsProcStatus = LdrGetProcedureAddress(
+ ModuleHandle,
+ &ProcedureName,
+ 0,
+ (PVOID *)&MsInitialize
+ );
+
+ if ( NT_SUCCESS(MsProcStatus) ) {
+ (MsInitialize)( &LsapPrivateLsaApi );
+ }
+ }
+
+
+
+ //
+ // if anything failed, unload the DLL.
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ IgnoreStatus = LdrUnloadDll( ModuleHandle );
+ }
+
+ return Status;
+
+
+}
+
+
+VOID
+LsapUnloadPackage()
+
+/*++
+
+Routine Description:
+
+ This function unloads an authentication package dll. This is expected
+ to be used only in the case where a package was successfully loaded,
+ but did not successfully initialize itself.
+
+ This function must be called with exclusive access to shared
+ authentication data (LsapAuLock() called).
+
+Arguments:
+
+ TBS
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ return;
+
+}
+
+NTSTATUS
+LsapAuApiDispatchLookupPackage(
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the dispatch routine for LsaLookupPackage().
+
+ This function locates and returns the package ID of the specified
+ authentication package.
+
+Arguments:
+
+ Request - Represents the client's LPC request message and context.
+ The request message contains a LSAP_LOOKUP_PACKAGE_ARGS message
+ block.
+
+ TrustedClient - Is this connection from a trusted client, one who has
+ TCB privilege.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+ STATUS_NO_SUCH_PACKAGE - The specified authentication package is
+ unknown to the LSA.
+
+
+--*/
+
+{
+
+ PLSAP_LOOKUP_PACKAGE_ARGS Arguments;
+ ULONG i;
+
+
+ Arguments = &ClientRequest->Request->Arguments.LookupPackage;
+
+
+ LsapAuLock();
+
+
+ //
+ // Look at each loaded package for a name match
+ //
+
+
+ i = 0;
+ while ( i < LsapPackageCount ) {
+
+ if ( (LsapPackageArray->Package[i]->Name->Length ==
+ Arguments->PackageNameLength) &&
+ (_strnicmp (
+ LsapPackageArray->Package[i]->Name->Buffer,
+ Arguments->PackageName,
+ Arguments->PackageNameLength
+ ) == 0 )
+ ) {
+
+ Arguments->AuthenticationPackage = i;
+
+ LsapAuUnlock();
+
+ return STATUS_SUCCESS;
+ }
+
+ i += 1;
+ }
+
+ LsapAuUnlock();
+ return STATUS_NO_SUCH_PACKAGE;
+
+}
+
+
+NTSTATUS
+LsapAuApiDispatchCallPackage(
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the dispatch routine for LsaCallPackage().
+
+Arguments:
+
+ Request - Represents the client's LPC request message and context.
+ The request message contains a LSAP_CALL_PACKAGE_ARGS message
+ block.
+
+ TrustedClient - Is this connection from a trusted client, one who has
+ TCB privilege. For untrusted clients call the
+ LsapApCallPackageUntrusted API and for trusted clients call teh
+ LsapApCallPackage API.
+
+
+Return Value:
+
+ In addition to the status values that an authentication package
+ might return, this routine will return the following:
+
+ STATUS_QUOTA_EXCEEDED - This error indicates that the call could
+ not be completed because the client does not have sufficient
+ quota to allocate the return buffer.
+
+ STATUS_NO_SUCH_PACKAGE - The specified authentication package is
+ unknown to the LSA.
+
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PLSAP_CALL_PACKAGE_ARGS Arguments;
+ PLSA_PACKAGE_TABLE PackageApi;
+ PVOID LocalProtocolSubmitBuffer; // Receives a copy of protocol submit buffer
+
+
+ Arguments = &ClientRequest->Request->Arguments.CallPackage;
+
+
+ //
+ // Get the address of the package to call
+ //
+
+ LsapAuLock();
+
+ if ( Arguments->AuthenticationPackage >= LsapPackageCount ) {
+ LsapAuUnlock();
+ return STATUS_NO_SUCH_PACKAGE;
+ }
+
+ PackageApi =
+ &LsapPackageArray->Package[Arguments->AuthenticationPackage]->PackageApi;
+
+ LsapAuUnlock();
+
+
+ //
+ // Fetch a copy of the profile buffer from the client's
+ // address space.
+ //
+
+ if (Arguments->SubmitBufferLength != 0) {
+
+ LocalProtocolSubmitBuffer =
+ LsapAllocateLsaHeap( Arguments->SubmitBufferLength );
+
+ Status = LsapCopyFromClientBuffer (
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ Arguments->SubmitBufferLength,
+ LocalProtocolSubmitBuffer,
+ Arguments->ProtocolSubmitBuffer
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ DbgPrint("LSA/CallPackage(): Failed to retrieve submit buffer %lx\n",Status);
+ return Status;
+ }
+
+ } else {
+ LocalProtocolSubmitBuffer = NULL;
+ }
+
+ ASSERT(ClientRequest->LogonProcessContext->CommPort != NULL);
+
+
+ //
+ // Now call the package. For trusted clients, call the normal
+ // CallPackage API. For untrusted clients, use the untrusted version.
+ //
+
+ if (TrustedClient) {
+ Status = (PackageApi->LsapApCallPackage)(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ LocalProtocolSubmitBuffer,
+ Arguments->ProtocolSubmitBuffer,
+ Arguments->SubmitBufferLength,
+ &Arguments->ProtocolReturnBuffer,
+ &Arguments->ReturnBufferLength,
+ &Arguments->ProtocolStatus
+ );
+
+ } else if (PackageApi->LsapApCallPackageUntrusted != NULL) {
+ Status = (PackageApi->LsapApCallPackageUntrusted)(
+ (PLSA_CLIENT_REQUEST)ClientRequest,
+ LocalProtocolSubmitBuffer,
+ Arguments->ProtocolSubmitBuffer,
+ Arguments->SubmitBufferLength,
+ &Arguments->ProtocolReturnBuffer,
+ &Arguments->ReturnBufferLength,
+ &Arguments->ProtocolStatus
+ );
+
+ } else {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+
+ //
+ // Free the local copy of the protocol submit buffer
+ //
+
+ if (LocalProtocolSubmitBuffer != NULL) {
+ LsapFreeLsaHeap( LocalProtocolSubmitBuffer );
+ }
+
+
+ return Status;
+
+}
diff --git a/private/lsa/server/auproc.c b/private/lsa/server/auproc.c
new file mode 100644
index 000000000..1309e3f4f
--- /dev/null
+++ b/private/lsa/server/auproc.c
@@ -0,0 +1,442 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ auproc.c
+
+Abstract:
+
+ This module provides logon process context management services within the
+ LSA subsystem.
+
+Author:
+
+ Jim Kelly (JimK) 11-March-1991
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "ausrvp.h"
+#include "adtp.h"
+
+
+
+
+NTSTATUS
+LsapValidLogonProcess(
+ IN PCLIENT_ID ClientId,
+ IN PLSAP_AU_REGISTER_CONNECT_INFO ConnectInfo,
+ OUT PLSAP_LOGON_PROCESS *LogonProcessContext
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks to see if a calling process qualifies as a logon
+ process. If so, a logon process context is created for the caller and
+ returned.
+
+ A logon process must hold the SeTcbPrivilege privilege. Since there
+ is no way to impersonate a connection requestor (that would be way
+ too easy), we have to open the client thread and then open that thread's
+ token.
+
+ If the ConnectInfo message is all zeros, then the client is asking
+ for an untrusted connection and the privilege check is omitted.
+
+Arguments:
+
+ ClientId - Pointer to the client Id of the sender of the logon
+ message. This is used to locate and open the calling thread or
+ process.
+
+ ConnectInfo - Authentication port information.
+
+ LogonProcessContext - If the caller is a legitimate logon process,
+ this receives a pointer to new logon process context block.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the caller is a legitimate logon process
+ and a logon process context block is being returned.
+
+ any other value - Indicates the caller is NOT a legitimate logon
+ process and a logon process context block is NOT being returned.
+ The value returned indicates the reason why the client is not
+ acceptable.
+
+--*/
+
+{
+
+ NTSTATUS Status, TempStatus;
+ BOOLEAN PrivilegeHeld;
+ HANDLE ClientThread, ClientProcess, ClientToken;
+ PRIVILEGE_SET Privilege;
+ OBJECT_ATTRIBUTES NullAttributes;
+ UNICODE_STRING Unicode;
+ STRING Ansi;
+ LSAP_AU_REGISTER_CONNECT_INFO NullConnectInfo;
+
+ RtlZeroMemory(
+ &NullConnectInfo,
+ sizeof(NullConnectInfo)
+ );
+
+
+ InitializeObjectAttributes( &NullAttributes, NULL, 0, NULL, NULL );
+
+ //
+ // Open the client process. This is needed to:
+ //
+ // 1) Access the client's virtual memory (to copy arguments),
+ // 2) Duplicate token handles into the process,
+ // 3) Open the process's token to see if it qualifies as
+ // a logon process.
+ //
+
+ Status = NtOpenProcess(
+ &ClientProcess,
+ PROCESS_QUERY_INFORMATION | // To open primary token
+ PROCESS_VM_OPERATION | // To allocate memory
+ PROCESS_VM_READ | // To read memory
+ PROCESS_VM_WRITE | // To write memory
+ PROCESS_DUP_HANDLE, // To duplicate a handle into
+ &NullAttributes,
+ ClientId
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+
+ //
+ // If the connect message is all zeros, setup an untrusted connection.
+ //
+
+ if (RtlCompareMemory(
+ &NullConnectInfo,
+ ConnectInfo,
+ sizeof(NullConnectInfo)) == sizeof(NullConnectInfo)) {
+
+ //
+ // Allocate a mostly empty fill in a new logon process context.
+ //
+
+ (*LogonProcessContext) =
+ LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_LOGON_PROCESS) );
+ if ( (*LogonProcessContext) == NULL ) {
+ TempStatus = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(TempStatus) );
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ RtlZeroMemory(
+ *LogonProcessContext,
+ sizeof(LSAP_LOGON_PROCESS)
+ );
+
+ //
+ // Save the handle to the client process.
+ // The CommPort field of LogonProcessContext will be filled in
+ // when the connection is accepted.
+ //
+
+ (*LogonProcessContext)->ClientProcess = ClientProcess;
+
+
+ (*LogonProcessContext)->TrustedClient = FALSE;
+
+ return(STATUS_SUCCESS);
+
+
+ }
+
+
+ //
+ // Open the client thread and that thread's token
+ //
+
+
+ Status = NtOpenThread(
+ &ClientThread,
+ THREAD_QUERY_INFORMATION,
+ &NullAttributes,
+ ClientId
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ TempStatus = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(TempStatus) );
+ return Status;
+ }
+
+ Status = NtOpenThreadToken(
+ ClientThread,
+ TOKEN_QUERY,
+ TRUE,
+ &ClientToken
+ );
+
+ TempStatus = NtClose( ClientThread );
+ ASSERT( NT_SUCCESS(TempStatus) );
+
+ //
+ // Make sure we succeeded in opening the token
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ if ( Status != STATUS_NO_TOKEN ) {
+ TempStatus = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(TempStatus) );
+ return Status;
+
+ } else {
+
+ //
+ // The thread isn't impersonating...open the process's token.
+ //
+
+ Status = NtOpenProcessToken(
+ ClientProcess,
+ TOKEN_QUERY,
+ &ClientToken
+ );
+
+
+ //
+ // Make sure we succeeded in opening the token
+ //
+
+ if ( !NT_SUCCESS(Status) ) {
+ TempStatus = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(TempStatus) );
+ return Status;
+ }
+
+ }
+
+ }
+
+ //
+ // OK, we have a token open
+ //
+
+
+
+ //
+ // Check for the privilege to execute this service.
+ //
+
+ Privilege.PrivilegeCount = 1;
+ Privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ Privilege.Privilege[0].Luid = LsapTcbPrivilege;
+ Privilege.Privilege[0].Attributes = 0;
+
+ Status = NtPrivilegeCheck(
+ ClientToken,
+ &Privilege,
+ &PrivilegeHeld
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // Generate any necessary audits
+ //
+
+ TempStatus = NtPrivilegedServiceAuditAlarm (
+ &LsapLsaAuName,
+ &LsapRegisterLogonServiceName,
+ ClientToken,
+ &Privilege,
+ PrivilegeHeld
+ );
+ // ASSERT( NT_SUCCESS(TempStatus) );
+
+ TempStatus = NtClose( ClientToken );
+ ASSERT( NT_SUCCESS(TempStatus) );
+
+ if ( !PrivilegeHeld ) {
+ TempStatus = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(TempStatus) );
+ return STATUS_PRIVILEGE_NOT_HELD;
+ }
+
+
+ //
+ // Convert the LogonProcessName to Unicode.
+ //
+
+ Ansi.Buffer = ConnectInfo->LogonProcessName;
+ Ansi.Length = Ansi.MaximumLength =
+ (USHORT) ConnectInfo->LogonProcessNameLength;
+ Status = RtlAnsiStringToUnicodeString( &Unicode, &Ansi, TRUE );
+
+ if ( !NT_SUCCESS( Status )) {
+ TempStatus = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(TempStatus) );
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+
+
+
+
+
+ //
+ // Allocate and fill in a new logon process context.
+ //
+
+ (*LogonProcessContext) =
+ LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_LOGON_PROCESS) +
+ Unicode.Length );
+ if ( (*LogonProcessContext) == NULL ) {
+ RtlFreeUnicodeString( &Unicode );
+ TempStatus = NtClose( ClientProcess );
+ ASSERT( NT_SUCCESS(TempStatus) );
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ //
+ // Save the handle to the client process.
+ // The CommPort field of LogonProcessContext will be filled in
+ // when the connection is accepted.
+ //
+
+ (*LogonProcessContext)->ClientProcess = ClientProcess;
+
+
+ //
+ // Save the LogonProcessName in the context
+ //
+
+ RtlCopyMemory( (*LogonProcessContext)->LogonProcessName,
+ Unicode.Buffer,
+ Unicode.Length );
+ (*LogonProcessContext)->LogonProcessName[Unicode.Length/sizeof(WCHAR)] = L'\0';
+
+ //
+ // Set the contex to be trusted.
+ //
+
+ (*LogonProcessContext)->TrustedClient = TRUE;
+
+ //
+ // Audit the registration of the logon process
+ //
+
+ LsapAdtAuditLogonProcessRegistration( ConnectInfo );
+ RtlFreeUnicodeString( &Unicode );
+
+
+
+ return(STATUS_SUCCESS);
+}
+
+NTSTATUS
+LsapAuApiDeregisterLogonProcess(
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function doesn't do anything. All the interesting
+ things are done as a result of dereferencing the client's
+ context, and that is done in auloop.c
+
+
+
+Arguments:
+
+ ClientRequest - Represents the client's LPC request message and context.
+ The request message contains a LSAP_AU_API_MESSAGE message
+ block.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+
+--*/
+
+{
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+
+NTSTATUS
+LsapAuRundownLogonProcess(
+ PLSAP_LOGON_PROCESS Context
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs Logon process rundown.
+ It is to be called if a logon process exits without deregistering.
+
+ This function deletes the logon process context specified
+ by the caller.
+
+
+Arguments:
+
+ Context - The context of the logon process to run-down.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+
+
+ //
+ // Close the client process and the communication port used
+ // to talk with this client.
+ //
+
+ Status = NtClose( Context->ClientProcess );
+#if DBG
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("LsaSrv: Auproc.c - Close of logon process failed, 0x%lx\n", Status);
+ }
+#endif //DBG
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtClose( Context->CommPort );
+#if DBG
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("LsaSrv: Auproc.c - Close of comm port failed, 0x%lx\n", Status);
+ }
+#endif //DBG
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // And free the client's context block.
+ //
+
+ LsapFreeLsaHeap( Context );
+
+ return(Status);
+
+}
diff --git a/private/lsa/server/ausess.c b/private/lsa/server/ausess.c
new file mode 100644
index 000000000..7fc1fc0b1
--- /dev/null
+++ b/private/lsa/server/ausess.c
@@ -0,0 +1,953 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ausess.c
+
+Abstract:
+
+ This module provides logon session management services within the
+ LSA subsystem. Some of these services are indirectly available for use
+ by authentication packages.
+
+Author:
+
+ Jim Kelly (JimK) 27-February-1991
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "ausrvp.h"
+#include "adtp.h"
+
+
+BOOLEAN
+LsapLogonSessionInitialize()
+
+/*++
+
+Routine Description:
+
+ This function initializes the LSA logon session database (an in-memory
+ structure).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PLSAP_LOGON_SESSION NewSession;
+
+ //
+ // Initialize the logon session list to contain one entry for the
+ // system logon ID. The corresponding reference monitor entry for
+ // this entry already exists, so just create the lsa record.
+ //
+
+ LsapLogonSessionList = NULL;
+
+
+ NewSession = LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_LOGON_SESSION) );
+ ASSERT ( NewSession != NULL );
+
+ RtlZeroMemory(
+ NewSession,
+ sizeof(LSAP_LOGON_SESSION)
+ );
+
+ NewSession->Packages = NULL;
+
+ //
+ // Fill in the account name from the well known sids
+ //
+
+ NewSession->AccountName = LsapAllocateLsaHeap(sizeof(UNICODE_STRING));
+ if (NewSession->AccountName == NULL) {
+ goto Cleanup;
+ }
+
+ NewSession->AccountName->Buffer = LsapAllocateLsaHeap(LsapDbWellKnownSidName(LsapLocalSystemSidIndex)->Length + sizeof(WCHAR));
+ if (NewSession->AccountName->Buffer == NULL) {
+ goto Cleanup;
+ }
+
+ NewSession->AccountName->MaximumLength = LsapDbWellKnownSidName(LsapLocalSystemSidIndex)->Length + sizeof(WCHAR);
+ RtlCopyUnicodeString(
+ NewSession->AccountName,
+ LsapDbWellKnownSidName(LsapLocalSystemSidIndex)
+ );
+
+ //
+ // Fill in the authority name from the well known sids
+ //
+ NewSession->AuthorityName = LsapAllocateLsaHeap(sizeof(UNICODE_STRING));
+ if (NewSession->AuthorityName == NULL) {
+ goto Cleanup;
+ }
+
+ NewSession->AuthorityName->Buffer = LsapAllocateLsaHeap(LsapDbWellKnownSidDescription(LsapLocalSystemSidIndex)->Length + sizeof(WCHAR));
+ if (NewSession->AuthorityName->Buffer == NULL) {
+ goto Cleanup;
+ }
+
+ NewSession->AuthorityName->MaximumLength = LsapDbWellKnownSidDescription(LsapLocalSystemSidIndex)->Length + sizeof(WCHAR);
+ RtlCopyUnicodeString(
+ NewSession->AuthorityName,
+ LsapDbWellKnownSidDescription(LsapLocalSystemSidIndex)
+ );
+
+ NewSession->UserSid = LsapAllocateLsaHeap(RtlLengthSid(LsapLocalSystemSid));
+ if (NewSession->UserSid == NULL) {
+ goto Cleanup;
+ }
+
+ RtlCopyMemory(
+ NewSession->UserSid,
+ LsapLocalSystemSid,
+ RtlLengthSid(LsapLocalSystemSid)
+ );
+
+ NewSession->LogonType = 0;
+ NewSession->LicenseHandle = INVALID_HANDLE_VALUE;
+ RtlCopyLuid( &NewSession->LogonId, &LsapSystemLogonId );
+
+ NewSession->NextSession = LsapLogonSessionList;
+ LsapLogonSessionList = NewSession;
+
+
+ return TRUE;
+Cleanup:
+ if (NewSession != NULL) {
+ if (NewSession->AccountName != NULL) {
+ if (NewSession->AccountName->Buffer != NULL)
+ {
+ LsapFreeLsaHeap(NewSession->AccountName->Buffer);
+ }
+ LsapFreeLsaHeap(NewSession->AccountName);
+ }
+
+ if (NewSession->AuthorityName != NULL) {
+ if (NewSession->AuthorityName->Buffer != NULL)
+ {
+ LsapFreeLsaHeap(NewSession->AuthorityName->Buffer);
+ }
+ LsapFreeLsaHeap(NewSession->AuthorityName);
+ }
+ if (NewSession->UserSid != NULL) {
+ LsapFreeLsaHeap(NewSession->UserSid);
+ }
+ LsapFreeLsaHeap(NewSession);
+ }
+ return(FALSE);
+
+}
+
+
+NTSTATUS
+LsapCreateLogonSession(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This function adds a new logon session to the list of logon sessions.
+ This service acquires the AuLock.
+
+Arguments:
+
+ LogonId - The ID to assign to the new logon session.
+
+Return Value:
+
+ STATUS_SUCCESS - The logon session has been successfully deleted.
+
+ STATUS_LOGON_SESSION_COLLISION - The specified Logon ID is already in
+ use by another logon session.
+
+ STATUS_QUOTA_EXCEEDED - The request could not be fulfilled due to
+ memory quota limitations.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_LOGON_SESSION NextSession, NewSession;
+
+ LsapAuLock();
+
+ //
+ // First see if there is already a session with the specified
+ // logon ID
+ //
+
+ NextSession = LsapLogonSessionList;
+
+ while (NextSession != NULL) {
+ if ( RtlEqualLuid(&NextSession->LogonId,LogonId) ) {
+ LsapDiagPrint( AU_LOGON_SESSIONS,
+ ("LSA DIAG: Attempt to add logon session resulted in collision\n"
+ " with existing logon session.\n"
+ " Original session:\n"
+ " LogonId: [%d, %d]\n"
+ " Authority Name: *%wZ*\n"
+ " Account Name: *%wZ*\n",
+ LogonId->HighPart, LogonId->LowPart,
+ NextSession->AuthorityName,
+ NextSession->AccountName));
+ LsapAuUnlock();
+
+ return STATUS_LOGON_SESSION_COLLISION;
+ }
+
+ NextSession = NextSession->NextSession;
+
+ }
+
+
+ //
+ // Make the new logon session.
+ //
+
+ NewSession = LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_LOGON_SESSION) );
+ if ( NewSession == NULL ) {
+ LsapAuUnlock();
+ return STATUS_QUOTA_EXCEEDED;
+ }
+ NewSession->Packages = NULL;
+ NewSession->AccountName = NULL;
+ NewSession->AuthorityName = NULL;
+ NewSession->UserSid = NULL;
+ NewSession->LogonType = 0;
+ NewSession->LicenseHandle = INVALID_HANDLE_VALUE;
+ RtlCopyLuid( &NewSession->LogonId, LogonId );
+
+
+ //
+ // Tell the reference monitor about the logon session...
+ //
+
+ Status = LsapCallRm(
+ RmCreateLogonSession,
+ (PVOID)LogonId,
+ (ULONG)sizeof(LUID),
+ NULL,
+ 0
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ LsapAuUnlock();
+ LsapFreeLsaHeap( NewSession );
+
+ LsapDiagPrint( AU_LOGON_SESSIONS,
+ ("LSA DIAG: Inconsistent LSA/RM logon session databases encountered\n"
+ " while creating new logon session (ID: [%d, %d]).\n"
+ " Session already existed in RM but not in LSA\n",
+ LogonId->HighPart, LogonId->LowPart));
+
+ return Status;
+ }
+
+
+ LsapDiagPrint( AU_LOGON_SESSIONS,
+ ("LSA DIAG: New logon session created (ID: [%d, %d]).\n",
+ LogonId->HighPart, LogonId->LowPart));
+
+ //
+ // Add it to the LSA's list
+ //
+
+ NewSession->NextSession = LsapLogonSessionList;
+ LsapLogonSessionList = NewSession;
+
+
+ //
+ // done.
+ //
+
+
+ LsapAuUnlock();
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+LsapDeleteLogonSession (
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This function deletes a logon session context record. It is expected
+ that no TOKEN objects were ever created within this logon session.
+ This means we must inform the Reference Monitor to clean up its
+ information on the logon session.
+
+ If TOKEN objecs were created within this logon session, then deletion
+ of those tokens will cause the logon session to be deleted.
+
+ This service acquires the AuLock.
+
+
+Arguments:
+
+ LogonId - The ID of the logon session to delete.
+
+Return Value:
+
+ STATUS_SUCCESS - The logon session has been successfully deleted.
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session doesn't
+ exist.
+
+ STATUS_BAD_LOGON_SESSION_STATE - The logon session is not in a state
+ that allows it to be deleted. This is typically an indication
+ that the logon session has had a token created within it, and it
+ may no longer be explicitly deleted.
+
+--*/
+
+{
+
+ return ( LsapInternalDeleteLogonSession ( LogonId, TRUE ) );
+
+}
+
+
+NTSTATUS
+LsapInternalDeleteLogonSession (
+ IN PLUID LogonId,
+ IN BOOLEAN InformReferenceMonitor
+ )
+
+/*++
+
+Routine Description:
+
+ This function deletes a logon session context record.
+
+ Unlike LsapDeleteLogonSession(), this routine may be used whether
+ tokens have been created in the logon session or not. If logon
+ sessions have been created, then it is the reference monitor calling
+ the LSA that is causing the logon session to be deleted. In this
+ case, there is no need to inform the reference monitor about this
+ deletion.
+
+ Note: It is assumed that if the reference monitor does NOT have to
+ be informed of the logon session deletion, then the authentication
+ packages DO need to be informed.
+
+ This service acquires the AuLock.
+
+
+Arguments:
+
+ LogonId - The ID of the logon session to delete.
+
+ InformReferenceMonitor - A BOOLEAN indicating whether or not the reference
+ monitor must be told about this logon session deletion. TRUE indicates
+ the reference monitor should be notified.
+
+Return Value:
+
+ STATUS_SUCCESS - The logon session has been successfully deleted.
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session doesn't
+ exist.
+
+ STATUS_BAD_LOGON_SESSION_STATE - The logon session is not in a state
+ that allows it to be deleted. This is typically an indication
+ that the logon session has had a token created within it, and it
+ may no longer be explicitly deleted.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PLSAP_LOGON_SESSION GoodByeSession;
+ BOOLEAN AbnormalRequest = FALSE;
+
+
+
+
+ //
+ // Tell the reference monitor to remove its logon session tracking record.
+ // If we aren't suppose to tell the reference monitor, then we have to
+ // tell the authentication packages.
+ //
+
+ if ( InformReferenceMonitor ) {
+ Status = LsapCallRm(
+ RmDeleteLogonSession,
+ (PVOID)LogonId,
+ (ULONG)sizeof(LUID),
+ NULL,
+ 0
+ );
+
+ if ( !NT_SUCCESS(Status)) {
+ DbgPrint("LSA: Unexpected LSA/RM logon session request.\n");
+ DbgPrint(" Request received to notify RM to delete Logon Session\n"
+ " but session did not exist in reference monitor.\n");
+ AbnormalRequest = TRUE;
+ }
+ } else {
+
+ LsapAuLogonTerminatedPackages( LogonId );
+ }
+
+ //
+ // Query the logon session information for the logoff audit.
+ //
+ // Big fat note:
+ //
+ // DO NOT remove the logon session from the logon session list,
+ // because if you do, we will get no useful information in the logoff
+ // audit.
+ //
+
+ LsapAuLock();
+
+ GoodByeSession = LsapGetLogonSession( LogonId, FALSE );
+
+ //
+ // If the GoodBye record isn't found, then return error.
+ // Otherwise, go about deleting the sucker.
+ //
+
+ if ( GoodByeSession == NULL ) {
+ if ( NT_SUCCESS(Status)) {
+ DbgPrint("LSA: Inconsistent LSA/RM logon session tracking database.\n");
+ DbgPrint(" Session existed in reference monitor but not in LSA.\n");
+ }
+
+ LsapAuUnlock();
+ return STATUS_NO_SUCH_LOGON_SESSION;
+
+ }
+
+
+ //
+ // We aren't going to fail from here out, so generate the
+ // logoff audit if appropriate.
+ //
+
+ //
+ // Only generate the audit if InformReferenceMonitor is false,
+ // meaning a normal logoff has occured. Otherwise we are cleaning
+ // up after a bad logon attempt and the information we need is not
+ // in the logon session structure.
+ //
+ // Also make sure that the logon session structure contains what we
+ // need before we try to generate an audit with it.
+ //
+
+ if (LsapAuditSuccessfulLogons && !InformReferenceMonitor && GoodByeSession->UserSid != NULL) {
+
+ //
+ // Note that we don't care if this fails, since
+ // we aren't going to abort the logoff just because
+ // we couldn't audit it.
+ //
+
+ LsapAdtAuditLogoff( GoodByeSession );
+ }
+
+#if DBG
+ if (AbnormalRequest) {
+
+ //
+ // Print out some information about the logon session
+ //
+
+ DbgPrint("LSA: Abnormal logon session deletion:\n"
+ " LogonId: [%d, %d]\n"
+ " Authority Name: *%wZ*\n"
+ " Account Name: *%wZ*\n",
+ LogonId->HighPart, LogonId->LowPart,
+ GoodByeSession->AuthorityName,
+ GoodByeSession->AccountName );
+ }
+#endif //DBG
+
+ GoodByeSession = LsapGetLogonSession( LogonId, TRUE );
+
+ //
+ // This wasn't NULL before, it better not be now.
+ //
+
+ ASSERT(GoodByeSession != NULL);
+
+ LsapAuUnlock();
+
+ //
+ // Close the license held by the logon session.
+ //
+
+ LsaFreeLicenseHandle( GoodByeSession->LicenseHandle );
+
+ //
+ // Free credentials associated with the package.
+ //
+
+ LsapFreePackageCredentialList( GoodByeSession->Packages );
+
+
+
+ //
+ // Free account and authority names if necessary
+ //
+
+ if (GoodByeSession->AccountName != NULL) {
+ if (GoodByeSession->AccountName->Buffer != NULL) {
+ LsapFreeLsaHeap( GoodByeSession->AccountName->Buffer );
+ }
+ LsapFreeLsaHeap( GoodByeSession->AccountName );
+ }
+
+ if (GoodByeSession->AuthorityName != NULL) {
+ if (GoodByeSession->AuthorityName->Buffer != NULL) {
+ LsapFreeLsaHeap( GoodByeSession->AuthorityName->Buffer );
+ }
+ LsapFreeLsaHeap( GoodByeSession->AuthorityName );
+ }
+
+ if (GoodByeSession->UserSid != NULL) {
+ LsapFreeLsaHeap( GoodByeSession->UserSid );
+ }
+
+
+ //
+ // Free the logon session record itself.
+ //
+
+ LsapFreeLsaHeap( GoodByeSession );
+
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+PLSAP_LOGON_SESSION
+LsapGetLogonSession (
+ IN PLUID LogonId,
+ IN BOOLEAN RemoveFromList
+ )
+
+/*++
+
+Routine Description:
+
+ This function retrieves a pointer to the specified logon session.
+ It will optionally remove the session from the list of sessions.
+
+ This routine must be called with the AuLock held.
+
+Arguments:
+
+ LogonId - The ID of the logon session to get.
+
+ RemoveFromList - A boolean indicating whether the session record
+ is to be removed from the list (TRUE) or left in the list (FALSE).
+
+Return Value:
+
+ NON-NULL - A pointer to the specified logon session.
+
+ NULL - No such logon session exists.
+
+--*/
+
+{
+
+ PLSAP_LOGON_SESSION *NextSession, TargetSession;
+
+
+ //
+ // See if the session exists
+ //
+
+ NextSession = &LsapLogonSessionList;
+
+ TargetSession = NULL;
+ while ((*NextSession) != NULL) {
+ if ( RtlEqualLuid(&(*NextSession)->LogonId,LogonId) ) {
+
+ //
+ // Found it
+ //
+
+ TargetSession = (*NextSession);
+
+ //
+ // Remove the specified session from the list?
+ //
+
+ if (RemoveFromList) {
+ (*NextSession) = TargetSession->NextSession;
+
+ LsapDiagPrint( AU_LOGON_SESSIONS,
+ ("LSA DIAG: Removing logon session from list in LSA.\n"
+ " LogonId: [%d, %d]\n"
+ " Authority Name: *%wZ*\n"
+ " Account Name: *%wZ*\n",
+ LogonId->HighPart, LogonId->LowPart,
+ TargetSession->AuthorityName,
+ TargetSession->AccountName ) );
+
+ }
+
+
+ return TargetSession;
+
+ }
+
+ //
+ // Move on to next session.
+ //
+
+ NextSession = &(*NextSession)->NextSession;
+
+ }
+
+ return NULL;
+
+}
+
+
+NTSTATUS
+LsapLogonSessionDeletedWrkr(
+ IN PLSA_COMMAND_MESSAGE CommandMessage,
+ OUT PLSA_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by the reference monitor (via LPC) when the
+ reference count on a logon session drops to zero. This indicates that
+ the logon session is no longer needed. This is technically when the
+ user is considered (from a security standpoint) to be logged out.
+
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing LSA command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (LsapComponentTestCommand).
+
+ The command-specific portion of this parameter contains the
+ LogonId (LUID) of the logon session whose reference count
+ has dropped to zero.
+
+ ReplyMessage - Pointer to structure containing LSA reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LUID LogonId;
+
+
+ //
+ // Check that command is expected type
+ //
+
+ ASSERT( CommandMessage->CommandNumber == LsapLogonSessionDeletedCommand );
+
+
+
+
+ //
+ // Typecast the command parameter to what we expect.
+ //
+
+ LogonId = *((LUID *) CommandMessage->CommandParams);
+
+
+
+ //
+ // Delete the LSA portion of the logon session record.
+ // Don't notify the reference monitor (since it is notifying us).
+ //
+
+ Status = LsapInternalDeleteLogonSession( &LogonId, FALSE );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+
+ UNREFERENCED_PARAMETER(ReplyMessage); // Intentionally not referenced
+ return(Status);
+
+}
+
+
+
+NTSTATUS
+LsapSetLogonSessionAccountInfo (
+ IN PLUID LogonId,
+ IN PUNICODE_STRING AccountName,
+ IN PUNICODE_STRING AuthorityName,
+ IN PSID UserSid,
+ IN SECURITY_LOGON_TYPE LogonType
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets username and authentication domain information
+ for a specified logon session.
+
+ The current account name and authority name, if any, will be freed.
+
+Arguments:
+
+ LogonId - The ID of the logon session to set.
+
+ AccountName - points to a unicode string containing the account name
+ to be assigned to the logon session. Both the UNICODE_STRING
+ structure and the buffer pointed to by that structure are expected
+ to be allocated from lsa heap, and they will eventually be freed
+ to that heap when no longer needed.
+
+ AuthorityName - points to a unicode string containing the name of the
+ authenticating authority of the logon session. Both the
+ UNICODE_STRING structure and the buffer pointed to by that structure
+ are expected to be allocated from lsa heap, and they will eventually
+ be freed to that heap when no longer needed.
+
+
+
+Return Value:
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session does
+ not currently exist.
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PLSAP_LOGON_SESSION LogonSession;
+
+
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+
+ LsapAuLock();
+
+
+ LogonSession = LsapGetLogonSession ( LogonId, FALSE );
+
+ if (LogonSession != NULL) {
+
+ Status = STATUS_SUCCESS;
+
+
+ //
+ // Free current names if necessary
+ //
+
+ if (LogonSession->AccountName != NULL) {
+ if (LogonSession->AccountName->Buffer != NULL) {
+ LsapFreeLsaHeap( LogonSession->AccountName->Buffer );
+ }
+ LsapFreeLsaHeap( LogonSession->AccountName );
+ }
+
+ if (LogonSession->AuthorityName != NULL) {
+ if (LogonSession->AuthorityName->Buffer != NULL) {
+ LsapFreeLsaHeap( LogonSession->AuthorityName->Buffer );
+ }
+ LsapFreeLsaHeap( LogonSession->AuthorityName );
+ }
+
+ if (LogonSession->UserSid != NULL) {
+ LsapFreeLsaHeap( LogonSession->UserSid );
+ }
+
+
+ //
+ // Assign the new names - they may be null
+ //
+
+ LogonSession->AccountName = AccountName;
+ LogonSession->AuthorityName = AuthorityName;
+ LogonSession->UserSid = UserSid;
+ LogonSession->LogonType = LogonType;
+
+
+ }
+
+
+ LsapAuUnlock();
+
+ return(Status);
+
+}
+
+
+
+NTSTATUS
+LsapGetLogonSessionAccountInfo (
+ IN PLUID LogonId,
+ OUT PUNICODE_STRING AccountName,
+ OUT PUNICODE_STRING AuthorityName
+ )
+
+/*++
+
+Routine Description:
+
+ This function retrieves username and authentication domain information
+ for a specified logon session.
+
+
+Arguments:
+
+ LogonId - The ID of the logon session to set.
+
+ AccountName - points to a unicode string with no buffer. A buffer
+ containing the account name will be allocated and returned
+ using the PROCESS HEAP - NOT THE LSA HEAP.
+
+ AuthorityName - points to a unicode string with no buffer. A buffer
+ containing the authority name will be allocated and returned
+ using the PROCESS HEAP - NOT THE LSA HEAP.
+
+
+
+Return Value:
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session does
+ not currently exist.
+
+ STATUS_NO_MEMORY - Could not allocate enough process heap.
+
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PLSAP_LOGON_SESSION LogonSession;
+
+
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+
+ AccountName->Length = 0;
+ AccountName->Buffer = NULL;
+ AccountName->MaximumLength = 0;
+
+ AuthorityName->Length = 0;
+ AuthorityName->Buffer = NULL;
+ AuthorityName->MaximumLength = 0;
+
+
+ LsapAuLock();
+
+ LogonSession = LsapGetLogonSession ( LogonId, FALSE );
+
+ if (LogonSession != NULL) {
+
+ Status = STATUS_SUCCESS;
+
+
+
+ //
+ // See if there is an account name.
+ // if not, provide a null string.
+ //
+
+
+ if (LogonSession->AccountName != NULL) {
+
+ if (LogonSession->AccountName->Length > 0) {
+
+ AccountName->MaximumLength = LogonSession->AccountName->MaximumLength;
+ AccountName->Buffer = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ AccountName->MaximumLength
+ );
+ if (AccountName->Buffer == NULL) {
+
+ Status = STATUS_NO_MEMORY;
+ } else {
+ RtlCopyUnicodeString( AccountName, LogonSession->AccountName );
+ } //end_if
+ } //end_if
+
+ } //end_if
+
+
+ //
+ // Now the authority name.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ if (LogonSession->AuthorityName != NULL) {
+
+ if (LogonSession->AuthorityName->Length > 0) {
+
+ AuthorityName->MaximumLength = LogonSession->AuthorityName->MaximumLength;
+ AuthorityName->Buffer = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ AuthorityName->MaximumLength
+ );
+ if (AuthorityName->Buffer == NULL) {
+ if (AccountName->Buffer != NULL) {
+ RtlFreeHeap( RtlProcessHeap(), 0, AccountName->Buffer );
+ AccountName->Buffer = NULL;
+ AccountName->MaximumLength = 0;
+ }
+ Status = STATUS_NO_MEMORY;
+ } else {
+ RtlCopyUnicodeString( AuthorityName, LogonSession->AuthorityName );
+ } //end_if
+ } //end_if
+
+ } //end_if
+
+ } //end_if
+
+ } //end_if (LogonSession != NULL)
+
+
+ LsapAuUnlock();
+
+ return(Status);
+
+}
+
+
diff --git a/private/lsa/server/ausrvp.h b/private/lsa/server/ausrvp.h
new file mode 100644
index 000000000..a170ac8b2
--- /dev/null
+++ b/private/lsa/server/ausrvp.h
@@ -0,0 +1,889 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ausrvp.h
+
+Abstract:
+
+ This module contains AUTHENTICATION related data structures and
+ API definitions that are private to the Local Security Authority
+ (LSA) server.
+
+
+Author:
+
+ Jim Kelly (JimK) 21-February-1991
+
+Revision History:
+
+--*/
+
+#ifndef _AUSRVP_
+#define _AUSRVP_
+
+//#define LSAP_AU_TRACK_CONTEXT
+//#define LSAP_AU_TRACK_THREADS
+//#define LSAP_AU_TRACK_LOGONS
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntlsa.h>
+#include <ntdbg.h>
+#include <stdlib.h>
+#include "lsasrvp.h"
+#include "aup.h"
+#include <samrpc.h>
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// AU specific constants //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+//
+// The filter/augmentor routines use the following bits in a mask
+// to track properties of IDs during logon. These bits have the following
+// meaning:
+//
+// LSAP_AU_SID_PROP_ALLOCATED - Indicates the SID was allocated within
+// the filter routine. If an error occurs, this allows allocated
+// IDs to be deallocated. Otherwise, the caller must deallocate
+// them.
+//
+// LSAP_AU_SID_COPY - Indicates the SID must be copied before returning.
+// This typically indicates that the pointed-to SID is a global
+// variable for use throughout LSA or that the SID is being referenced
+// from another structure (such as an existing TokenInformation structure).
+//
+// LSAP_AU_SID_PROP_HIGH_RATE - Indicates it is expected that the SID
+// will typically be used in ACLs to grant access. This is useful
+// to know when arranging SIDs. Placing the IDs that will have a
+// high chance of granting access at the front of the list of SIDs
+// will reduce the amount of time spent in access validation routines
+// after logon.
+//
+
+#define LSAP_AU_SID_PROP_ALLOCATED (0x00000001L)
+#define LSAP_AU_SID_PROP_COPY (0x00000002L)
+#define LSAP_AU_SID_PROP_HIGH_RATE (0x00000004L)
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Macro definitions //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// Macros to gain exclusive access to protected global authentication
+// data structures
+//
+
+#define LsapAuLock() (RtlEnterCriticalSection(&LsapAuLock))
+#define LsapAuUnlock() (RtlLeaveCriticalSection(&LsapAuLock))
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Type definitions //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+//
+// This data structure is used to house logon process information.
+//
+
+typedef struct _LSAP_LOGON_PROCESS {
+
+ //
+ // Links - Used to link contexts together. This must be the
+ // first field of the context block.
+ //
+
+ LIST_ENTRY Links;
+
+
+ //
+ // ReferenceCount - Used to prevent this context from being
+ // deleted prematurely.
+ //
+
+ ULONG References;
+
+
+ //
+ // ClientProcess - A handle to the client process. This handle is
+ // used to perform virtual memory operations within the client
+ // process (allocate, deallocate, read, write).
+ //
+
+ HANDLE ClientProcess;
+
+
+ //
+ // CommPort - A handle to the LPC communication port created to
+ // communicate with this client. this port must be closed
+ // when the client deregisters.
+ //
+
+ HANDLE CommPort;
+
+ //
+ // TrustedClient - If TRUE, the caller has TCB privilege and may
+ // call any API. If FALSE, the caller may only call
+ // LookupAuthenticatePackage and CallPackage, which is converted
+ // to LsaApCallPackageUntrusted.
+ //
+
+ BOOLEAN TrustedClient;
+
+ //
+ // Name of the logon process.
+ //
+
+ WCHAR LogonProcessName[1];
+
+} LSAP_LOGON_PROCESS, *PLSAP_LOGON_PROCESS;
+
+
+
+
+//
+// This structure should be treated as opaque by non-LSA code.
+// It is used to maintain client information related to individual
+// requests. A public data structure (LSA_CLIENT_REQUEST) is
+// typecast to this type by LSA code.
+//
+
+typedef struct _LSAP_CLIENT_REQUEST {
+
+ //
+ // Request - Points to the request message received from the
+ // client.
+ //
+
+ PLSAP_AU_API_MESSAGE Request;
+
+
+
+ //
+ // LogonProcessContext - Points to the logon process context
+ // established when the logon process registered.
+ //
+
+ PLSAP_LOGON_PROCESS LogonProcessContext;
+
+} LSAP_CLIENT_REQUEST, *PLSAP_CLIENT_REQUEST;
+
+
+
+
+
+//
+// The dispatch table of services which are provided by
+// authentication packages.
+//
+typedef struct _LSAP_PACKAGE_TABLE {
+ PLSA_AP_INITIALIZE_PACKAGE LsapApInitializePackage;
+ PLSA_AP_LOGON_USER LsapApLogonUser;
+ PLSA_AP_CALL_PACKAGE LsapApCallPackage;
+ PLSA_AP_LOGON_TERMINATED LsapApLogonTerminated;
+ PLSA_AP_CALL_PACKAGE_UNTRUSTED LsapApCallPackageUntrusted;
+ PLSA_AP_LOGON_USER_EX LsapApLogonUserEx;
+} LSAP_PACKAGE_TABLE, *PLSA_PACKAGE_TABLE;
+
+
+//
+// Used to house information about each loaded authentication package
+//
+
+typedef struct _LSAP_PACKAGE_CONTEXT {
+ PSTRING Name;
+ LSAP_PACKAGE_TABLE PackageApi;
+} LSAP_PACKAGE_CONTEXT, *PLSAP_PACKAGE_CONTEXT;
+
+
+//
+// Rather than keep authentication package contexts in a linked list,
+// they are pointed to via an array of pointers. This is practical
+// because there will never be more than a handful of authentication
+// packages in any particular system, and because authentication packages
+// are never unloaded.
+//
+
+typedef struct _LSAP_PACKAGE_ARRAY {
+ PLSAP_PACKAGE_CONTEXT Package[ANYSIZE_ARRAY];
+} LSAP_PACKAGE_ARRAY, *PLSAP_PACKAGE_ARRAY;
+
+
+
+
+//
+// Logon Session & Credential management data structures.
+//
+// Credentials are kept in a structure that looks like:
+//
+// +------+ +------+
+// LsapLogonSessions->| Logon|---->| Logon|------> o o o
+// | Id | | Id |
+// | * | | * |
+// +---|--+ +---|--+
+// |
+// | +-----+ +-----+
+// +-->| Auth|------>| Auth|
+// | Cred| | Cred|
+// |- - -| |- - -|
+// | Cred| | . |
+// | List| | . |
+// | * | | . |
+// +--|--+ +-----+
+// |
+// +------> +------------+
+// | NextCred | -----> o o o
+// |- - - - - - |
+// | Primary Key|--->(PrimaryKeyvalue)
+// |- - - - - - |
+// | Credential |
+// | Value |--->(CredentialValue)
+// +------------+
+//
+//
+//
+
+typedef struct _LSAP_CREDENTIALS {
+
+ struct _LSAP_CREDENTIALS *NextCredentials;
+ STRING PrimaryKey;
+ STRING Credentials;
+
+} LSAP_CREDENTIALS, *PLSAP_CREDENTIALS;
+
+
+
+typedef struct _LSAP_PACKAGE_CREDENTIALS {
+
+ struct _LSAP_PACKAGE_CREDENTIALS *NextPackage;
+
+ //
+ // Package that created (and owns) these credentials
+ //
+
+ ULONG PackageId;
+
+ //
+ // List of credentials associated with this package
+ //
+
+ PLSAP_CREDENTIALS Credentials;
+
+} LSAP_PACKAGE_CREDENTIALS, *PLSAP_PACKAGE_CREDENTIALS;
+
+
+typedef struct _LSAP_LOGON_SESSION {
+
+ //
+ // Used to link all LSAP_LOGON_SESSIONs together
+ //
+
+ struct _LSAP_LOGON_SESSION *NextSession;
+
+ //
+ // Each record represents just one logon session
+ //
+
+ LUID LogonId;
+
+
+ //
+ // For audit purposes, we keep an account name, authenticating
+ // authority name, and User SID for each logon session.
+ //
+
+ PUNICODE_STRING AccountName;
+ PUNICODE_STRING AuthorityName;
+ PSID UserSid;
+ SECURITY_LOGON_TYPE LogonType;
+
+
+ //
+ // The authentication packages that have credentials associated
+ // with this logon session each have their own record in the following
+ // linked list.
+ //
+
+ PLSAP_PACKAGE_CREDENTIALS Packages;
+
+ //
+ // License Server Handle.
+ //
+ // Null if the license server need not be notified upon logoff.
+ //
+
+ HANDLE LicenseHandle;
+
+} LSAP_LOGON_SESSION, *PLSAP_LOGON_SESSION;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Internal API definitions //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+//
+// Logon process context management services
+//
+
+NTSTATUS
+LsapAuInitializeContextMgr(
+ VOID
+ );
+
+VOID
+LsapAuAddClientContext(
+ PLSAP_LOGON_PROCESS Context
+ );
+
+BOOLEAN
+LsapAuReferenceClientContext(
+ PLSAP_CLIENT_REQUEST ClientRequest,
+ BOOLEAN RemoveContext,
+ PBOOLEAN TrustedClient
+ );
+
+VOID
+LsapAuDereferenceClientContext(
+ PLSAP_LOGON_PROCESS Context
+ );
+
+//
+// Authentication client loop and dispatch routines
+//
+
+
+NTSTATUS
+LsapAuListenLoop( // Listen for connections from logon processes
+ IN PVOID ThreadParameter
+ );
+
+NTSTATUS
+LsapAuServerLoop( // Wait for logon process calls & dispatch them
+ IN PVOID ThreadParameter
+ );
+
+
+BOOLEAN
+LsapAuLoopInitialize(
+ VOID
+ );
+
+
+
+typedef
+NTSTATUS // Template dispatch routine
+(* PLSAP_AU_API_DISPATCH)(
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ );
+
+NTSTATUS
+LsapAuApiDispatchLookupPackage( // LsaLookupPackage() dispatch routine
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ );
+
+NTSTATUS
+LsapAuApiDispatchLogonUser( // LsaLogonUser() dispatch routine
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ );
+
+NTSTATUS
+LsapAuApiDispatchCallPackage( // LsaCallAuthenticationPackage() dispatch routine
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ );
+
+NTSTATUS
+LsapAuApiDeregisterLogonProcess( // LsaDeregisterLogonProcess() dispatch routine
+ IN OUT PLSAP_CLIENT_REQUEST ClientRequest,
+ IN BOOLEAN TrustedClient
+ );
+
+
+NTSTATUS
+LsapAuRundownLogonProcess(
+ PLSAP_LOGON_PROCESS Context
+ );
+
+PSTRING
+LsapQueryPackageName(
+ PLSAP_PACKAGE_CONTEXT Package
+ );
+
+
+
+
+
+//
+// Private services available to Microsoft authentication packages.
+//
+
+
+BOOLEAN
+LsapAuMspInitialize(
+ VOID
+ );
+
+
+NTSTATUS
+LsapAuGetOperationalMode(
+ OUT PLSA_OPERATIONAL_MODE OperationalMode
+ );
+
+NTSTATUS
+LsapAuGetPrimaryDomain(
+ OUT PBOOLEAN PrimaryDomainDefined,
+ OUT PSTRING *PrimaryDomain
+ );
+
+
+
+//
+// Client process virtual memory routines
+//
+
+
+NTSTATUS
+LsapAllocateClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG LengthRequired,
+ OUT PVOID *ClientBaseAddress
+ );
+
+NTSTATUS
+LsapFreeClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ClientBaseAddress OPTIONAL
+ );
+
+NTSTATUS
+LsapCopyToClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG Length,
+ IN PVOID ClientBaseAddress,
+ IN PVOID BufferToCopy
+ );
+
+NTSTATUS
+LsapCopyFromClientBuffer (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN ULONG Length,
+ IN PVOID BufferToCopy,
+ IN PVOID ClientBaseAddress
+ );
+
+
+//
+// Logon session routines
+//
+
+
+BOOLEAN
+LsapLogonSessionInitialize();
+
+NTSTATUS
+LsapCreateLogonSession(
+ IN PLUID LogonId
+ );
+
+NTSTATUS
+LsapDeleteLogonSession (
+ IN PLUID LogonId
+ );
+
+NTSTATUS
+LsapInternalDeleteLogonSession (
+ IN PLUID LogonId,
+ IN BOOLEAN InformReferenceMonitor
+ );
+
+PLSAP_LOGON_SESSION
+LsapGetLogonSession (
+ IN PLUID LogonId,
+ IN BOOLEAN RemoveFromList
+ );
+
+NTSTATUS
+LsapSetLogonSessionAccountInfo(
+ IN PLUID LogonId,
+ IN PUNICODE_STRING AccountName,
+ IN PUNICODE_STRING AuthorityName,
+ IN PSID UserSid,
+ IN SECURITY_LOGON_TYPE LogonType
+ );
+
+NTSTATUS
+LsapGetLogonSessionAccountInfo(
+ IN PLUID LogonId,
+ OUT PUNICODE_STRING AccountName,
+ OUT PUNICODE_STRING AuthorityName
+ );
+
+
+
+
+
+//
+// Credentials routines
+//
+
+
+NTSTATUS
+LsapAddCredential(
+ IN PLUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN PSTRING PrimaryKeyValue,
+ IN PSTRING Credentials
+ );
+
+
+NTSTATUS
+LsapGetCredentials(
+ IN PLUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN OUT PULONG QueryContext,
+ IN BOOLEAN RetrieveAllCredentials,
+ IN PSTRING PrimaryKeyValue,
+ OUT PULONG PrimaryKeyLength,
+ IN PSTRING Credentials
+ );
+
+NTSTATUS
+LsapDeleteCredential(
+ IN PLUID LogonId,
+ IN ULONG AuthenticationPackage,
+ IN PSTRING PrimaryKeyValue
+ );
+
+
+PLSAP_PACKAGE_CREDENTIALS
+LsapGetPackageCredentials(
+ IN PLSAP_LOGON_SESSION LogonSession,
+ IN ULONG PackageId,
+ IN BOOLEAN CreateIfNecessary
+ );
+
+
+
+VOID
+LsapFreePackageCredentialList(
+ IN PLSAP_PACKAGE_CREDENTIALS PackageCredentialList
+ );
+
+
+
+VOID
+LsapFreeCredentialList(
+ IN PLSAP_CREDENTIALS CredentialList
+ );
+
+
+NTSTATUS
+LsapReturnCredential(
+ IN PLSAP_CREDENTIALS SourceCredentials,
+ IN PSTRING TargetCredentials,
+ IN BOOLEAN ReturnPrimaryKey,
+ IN PSTRING PrimaryKeyValue OPTIONAL,
+ OUT PULONG PrimaryKeyLength OPTIONAL
+ );
+
+
+
+//
+// Logon process related services
+//
+
+
+NTSTATUS
+LsapValidLogonProcess( // Test a process to see if it is a logon process
+ IN PCLIENT_ID ClientId,
+ IN PLSAP_AU_REGISTER_CONNECT_INFO ConnectInfo,
+ OUT PLSAP_LOGON_PROCESS *LogonProcessContext
+ );
+
+
+
+
+//
+// Authentication package routines
+//
+
+BOOLEAN
+LsapPackageInitialize();
+
+
+NTSTATUS
+LsapConfigurePackages();
+
+
+NTSTATUS
+LsapAddPackage(
+ IN PUNICODE_STRING PackageFileName,
+ IN PUNICODE_STRING DatabaseParameter,
+ IN PUNICODE_STRING ConfidentialityParameter
+ );
+
+NTSTATUS
+LsapLoadPackage(
+ IN PUNICODE_STRING PackageFileName,
+ IN PLSAP_PACKAGE_CONTEXT NewPackage
+ );
+
+VOID
+LsapUnloadPackage();
+
+
+VOID
+LsapAuLogonTerminatedPackages(
+ IN PLUID LogonId
+ );
+
+VOID
+LsaFreeLicenseHandle(
+ IN HANDLE LicenseHandle
+ );
+
+
+//
+// Miscellaneous other routines
+// (LsapAuInit() is the link to the rest of LSA and resides in lsap.h)
+//
+
+
+
+
+
+BOOLEAN
+LsapWellKnownValueInit(
+ VOID
+ );
+
+BOOLEAN
+LsapEnableCreateTokenPrivilege(
+ VOID
+ );
+
+
+
+
+NTSTATUS
+LsapCreateNullToken(
+ IN PLUID LogonId,
+ IN PTOKEN_SOURCE TokenSource,
+ IN PLSA_TOKEN_INFORMATION_NULL TokenInformationNull,
+ OUT PHANDLE Token
+ );
+
+NTSTATUS
+LsapCreateV1Token(
+ IN PLUID LogonId,
+ IN PTOKEN_SOURCE TokenSource,
+ IN PLSA_TOKEN_INFORMATION_V1 TokenInformationV1,
+ IN TOKEN_TYPE TokenType,
+ OUT PHANDLE Token
+ );
+
+
+NTSTATUS
+LsapCaptureClientTokenGroups(
+ IN PLSAP_CLIENT_REQUEST ClientRequest,
+ IN ULONG GroupCount,
+ IN PTOKEN_GROUPS ClientTokenGroups,
+ IN PTOKEN_GROUPS *CapturedTokenGroups
+ );
+
+
+VOID
+LsapFreeTokenGroups(
+ IN PTOKEN_GROUPS TokenGroups
+ );
+
+VOID
+LsapFreeTokenPrivileges(
+ IN PTOKEN_PRIVILEGES TokenPrivileges OPTIONAL
+ );
+
+VOID
+LsapFreeTokenInformationNull(
+ IN PLSA_TOKEN_INFORMATION_NULL TokenInformationNull
+ );
+
+
+
+VOID
+LsapFreeTokenInformationV1(
+ IN PLSA_TOKEN_INFORMATION_V1 TokenInformationV1
+ );
+
+
+
+NTSTATUS
+LsapIncorporateLocalGroups(
+ IN PTOKEN_GROUPS LocalGroups,
+ IN PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID *TokenInformation
+ );
+
+
+NTSTATUS
+LsapAuUserLogonPolicyFilter(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ IN PVOID *TokenInformation,
+ OUT PQUOTA_LIMITS QuotaLimits,
+ OUT PPRIVILEGE_SET *PrivilegesAssigned
+ );
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Global variables of the LSA server //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+//
+// Handle to LPC port used to communicate with logon processes.
+//
+
+HANDLE LsapAuApiPort;
+
+
+
+//
+// Dispatch table used by authentication packages to call back to
+// the LSA services available to them.
+//
+
+LSA_DISPATCH_TABLE LsapPackageDispatchTable;
+
+
+//
+// Dispatch table used by Microsoft authentication packages to call back to
+// the private LSA services available to them.
+//
+
+LSAP_PRIVATE_LSA_SERVICES LsapPrivateLsaApi;
+
+
+//
+// Lock protecting access to all authentication lists
+// and anything else needing exlusive access protection.
+//
+
+RTL_CRITICAL_SECTION LsapAuLock;
+
+
+//
+// Array of pointers to authentication package context blocks.
+// Also, the current count of loaded authentication packages.
+// This count also serves as the source of package IDs.
+//
+//
+
+ULONG LsapPackageCount;
+PLSAP_PACKAGE_ARRAY LsapPackageArray; // Actual array is allocated from heap
+
+
+
+//
+// List head for logon sessions.
+// Access protected by LsaAuLock.
+//
+
+PLSAP_LOGON_SESSION LsapLogonSessionList;
+
+
+//
+// List of logon processes who have registered with us.
+// Access protected by LsaAuLock.
+//
+
+LIST_ENTRY LsapLogonProcessList; // List head of logon processes
+
+
+
+//
+// Well known LUIDs
+//
+
+LUID LsapSystemLogonId;
+
+
+//
+// Well known privilege values
+//
+
+
+LUID LsapCreateTokenPrivilege;
+LUID LsapAssignPrimaryTokenPrivilege;
+LUID LsapLockMemoryPrivilege;
+LUID LsapIncreaseQuotaPrivilege;
+LUID LsapUnsolicitedInputPrivilege;
+LUID LsapTcbPrivilege;
+LUID LsapSecurityPrivilege;
+LUID LsapTakeOwnershipPrivilege;
+
+//
+// Strings needed for auditing.
+//
+
+UNICODE_STRING LsapLsaAuName;
+UNICODE_STRING LsapRegisterLogonServiceName;
+
+
+
+//
+// The following information pertains to the use of the local SAM
+// for authentication.
+//
+
+
+// Length of typical Sids of members of the Account or Built-In Domains
+
+ULONG LsapAccountDomainMemberSidLength,
+ LsapBuiltinDomainMemberSidLength;
+
+// Sub-Authority Counts for members of the Account or Built-In Domains
+
+UCHAR LsapAccountDomainSubCount,
+ LsapBuiltinDomainSubCount;
+
+// Typical Sids for members of Account or Built-in Domains
+
+PSID LsapAccountDomainMemberSid,
+ LsapBuiltinDomainMemberSid;
+
+
+
+
+
+#endif // _AUSRVP_
diff --git a/private/lsa/server/crserver.c b/private/lsa/server/crserver.c
new file mode 100644
index 000000000..5fd771dc5
--- /dev/null
+++ b/private/lsa/server/crserver.c
@@ -0,0 +1,113 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ crserver.c
+
+Abstract:
+
+ Local Security Authority - Server Cipher Routines
+
+ These routines interface the LSA server side with the Cipher
+ Routines. They perform RPC-style memory allocation.
+
+Author:
+
+ Scott Birrell (ScottBi) December 13, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <lsasrvp.h>
+
+
+NTSTATUS
+LsapCrServerGetSessionKey(
+ IN LSAPR_HANDLE ObjectHandle,
+ OUT PLSAP_CR_CIPHER_KEY *SessionKey
+ )
+
+/*++
+
+Routine Description:
+
+ This function obtains the Session Key, allocates an Cipher Key
+ structure and returns the key.
+
+Arguments:
+
+ ObjectHandle - Handle from an LsaOpen<ObjectType> call.
+
+ SessionKey - Receives a pointer to a structure containing the
+ Session Key in which the memory has been allocated via
+ MIDL_user_allocate().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ (e.g memory) to complete the call.
+--*/
+
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_CR_CIPHER_KEY OutputSessionKey = NULL;
+ ULONG OutputSessionKeyBufferLength;
+
+ //
+ // Allocate memory for the Session Key buffer and LSAP_CR_CIPHER_KEY
+ // structure.
+ //
+
+ OutputSessionKeyBufferLength = sizeof (USER_SESSION_KEY);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ OutputSessionKey = MIDL_user_allocate(
+ OutputSessionKeyBufferLength +
+ sizeof (LSAP_CR_CIPHER_KEY)
+ );
+
+ if (OutputSessionKey == NULL) {
+
+ goto ServerGetSessionKeyError;
+ }
+
+ //
+ // Fill in the Cipher key structure, making the buffer point to
+ // just beyond the header.
+ //
+
+ OutputSessionKey->Length = OutputSessionKeyBufferLength;
+ OutputSessionKey->MaximumLength = OutputSessionKeyBufferLength;
+ OutputSessionKey->Buffer = (PUCHAR) (OutputSessionKey + 1);
+
+ Status = RtlGetUserSessionKeyServer(
+ ObjectHandle,
+ (PUSER_SESSION_KEY) OutputSessionKey->Buffer
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ServerGetSessionKeyError;
+ }
+
+ OutputSessionKey->Length = OutputSessionKey->MaximumLength =
+ OutputSessionKeyBufferLength;
+
+ServerGetSessionKeyFinish:
+
+ *SessionKey = OutputSessionKey;
+ return(Status);
+
+ServerGetSessionKeyError:
+
+ goto ServerGetSessionKeyFinish;
+}
diff --git a/private/lsa/server/ctlsarm.c b/private/lsa/server/ctlsarm.c
new file mode 100644
index 000000000..10f7a962e
--- /dev/null
+++ b/private/lsa/server/ctlsarm.c
@@ -0,0 +1,244 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ctlsarm.c
+
+Abstract:
+
+ Local Security Authority Subsystem - CT Reference Monitor Communication.
+
+ NOTE: To run, substitute ctlsarm.exe for lsass.exe. ctlsarm.exe
+ behaves exactly like lsass.exe, except that the initial thread
+ goes on to run the CT variations before exiting.
+
+Author:
+
+ Scott Birrell (ScottBi) Mar 26, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#include "lsasrvp.h"
+
+//
+// Main is just a wrapper for the main initialization routine LsapInitLsa
+//
+
+VOID
+CtLsaRm(
+ );
+
+VOID
+CtRmLsa(
+ );
+
+VOID
+main ()
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Initialize the LSA. If successful, this routine won't return.
+ // If unsuccessful, we must exit with status so that the SM knows
+ // something has gone wrong.
+ //
+
+ if (!LsapInitLsa()) {
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Run CT Tests to send commands from LSA to Reference Monitor
+ //
+
+ CtLsaRm();
+
+ //
+ // Run CT Tests to send commands from Reference Monitor to LSA
+ //
+
+ CtRmLsa();
+
+ //
+ // Terminate this initialization thread. We leave behind the LSA
+ // Reference Monitor Command Server Thread.
+ //
+
+ NtTerminateThread( NtCurrentThread(), Status );
+}
+
+
+
+VOID
+CtLsaRm(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the Lsa to Rm command link.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None
+
+--*/
+
+
+{
+ NTSTATUS Status;
+
+ RM_COMMAND_MESSAGE RmCommandMessage;
+ PULONG CTParam = (ULONG *) RmCommandMessage.CommandParams;
+
+ DbgPrint("Security: Beginning CT for LSA to RM Communication\n");
+
+ //
+ // Var 1 - Send the Component Test Command to RM
+ //
+
+ *CTParam = RM_CT_COMMAND_PARAM_VALUE;
+
+ Status = LsapCallRm(
+ RmComponentTestCommand,
+ CTParam,
+ sizeof(ULONG),
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Var 1 - Send RM Component Test Command to RM failed %lx\n",
+ Status);
+ }
+
+ //
+ // Var 2 - Send Enable Audit Command to RM
+ //
+
+ Status = LsapCallRm(
+ RmEnableAuditCommand,
+ NULL,
+ 0,
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Var 2 - Send Enable Audit Command to RM failed %lx\n",
+ Status);
+ }
+
+ //
+ // Var 3 - Send Disable Audit Command to RM
+ //
+
+ Status = LsapCallRm(
+ RmDisableAuditCommand,
+ NULL,
+ 0,
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Var 3 - Send Disable Audit Command to RM failed %lx\n",
+ Status);
+ }
+
+ DbgPrint("Security: Ending CT for LSA to RM Communication\n");
+}
+
+
+
+VOID
+CtRmLsa(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the Rm to Lsa command link. The Lsa to Rm
+ command link must already be working. We send commands to LSA from
+ Rm by sending the special "Send Command Back To LSA" command to RM. This
+ command takes as parameters the LSA command number and parameters.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None
+
+--*/
+
+
+{
+
+ NTSTATUS Status;
+ RM_SEND_COMMAND_TO_LSA_PARAMS SendToLsaParams;
+ PULONG CTParam = (ULONG *) SendToLsaParams.LsaCommandParams;
+
+ DbgPrint("Security: Beginning CT for RM to LSA Communication\n");
+
+ //
+ // Var 1 - Send the Component Test Command to LSA
+ //
+
+ *CTParam = LSA_CT_COMMAND_PARAM_VALUE;
+
+ SendToLsaParams.LsaCommandNumber = LsapComponentTestCommand;
+ SendToLsaParams.LsaCommandParamsLength = sizeof(ULONG);
+ Status = LsapCallRm(
+ RmSendCommandToLsaCommand,
+ &SendToLsaParams,
+ sizeof(LSA_COMMAND_NUMBER) + sizeof(ULONG) +
+ SendToLsaParams.LsaCommandParamsLength,
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Var 1 - Send RM Component Test Command to RM failed %lx\n",
+ Status);
+ }
+
+ //
+ // Var 2 - Send Write Audit Message Command to LSA.
+ //
+
+ SendToLsaParams.LsaCommandNumber = LsapWriteAuditMessageCommand;
+ SendToLsaParams.LsaCommandParamsLength = 0;
+
+ Status = LsapCallRm(
+ RmSendCommandToLsaCommand,
+ &SendToLsaParams,
+ sizeof(LSA_COMMAND_NUMBER) + sizeof(ULONG) +
+ SendToLsaParams.LsaCommandParamsLength,
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Var 2 - Send Write Audit Message Command to LSA failed %lx\n",
+ Status);
+ }
+
+ // TBS
+
+ DbgPrint("Security: Ending CT for RM to LSA Communication\n");
+}
diff --git a/private/lsa/server/ctlsarpc.c b/private/lsa/server/ctlsarpc.c
new file mode 100644
index 000000000..484c50bdc
--- /dev/null
+++ b/private/lsa/server/ctlsarpc.c
@@ -0,0 +1,15305 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ctlsarpc.c
+
+Abstract:
+
+ Local Security Authority Subsystem - CT for RPC interface
+
+ This test exercises the LSA API that use RPC.
+
+Author:
+
+ Scott Birrell (ScottBi) April 26, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <lsasrvp.h>
+
+#include "ntrpcp.h"
+#include "samsrv.h"
+
+
+typedef enum _USERS {
+ Fred,
+ Wilma,
+ Pebbles,
+ Barney,
+ Betty,
+ Bambam,
+ Dino
+} USERS;
+
+typedef struct _CT_UNKNOWN_SID_ENTRY {
+
+ PSID Sid;
+ UNICODE_STRING Name;
+ UNICODE_STRING DomainName;
+ BOOLEAN DomainKnown;
+
+} CT_UNKNOWN_SID_ENTRY, *PCT_UNKNOWN_SID_ENTRY;
+
+#define CT_UNKNOWN_SID_COUNT ((ULONG) 0x00000040)
+
+typedef struct _CT_UNKNOWN_NAME_ENTRY {
+
+ UNICODE_STRING Name;
+ UNICODE_STRING DomainName;
+ PSID DomainSid;
+ BOOLEAN DomainKnown;
+
+} CT_UNKNOWN_NAME_ENTRY, *PCT_UNKNOWN_NAME_ENTRY;
+
+#define CT_UNKNOWN_NAME_COUNT ((ULONG) 0x00000040)
+
+//
+// Information for a SAM Domain account
+//
+
+typedef struct _CT_DOMAIN_ACCOUNT_INFO {
+
+ UNICODE_STRING Name;
+ ULONG Rid;
+
+} CT_DOMAIN_ACCOUNT_INFO, *PCT_DOMAIN_ACCOUNT_INFO;
+
+//
+// Enumeration Information returned on a single call to an enumeration
+// API.
+//
+
+typedef struct _CT_LSA_SINGLE_CALL_ENUM_INFO {
+
+ ULONG CountReturned;
+ PVOID EnumInfoReturned;
+
+} CT_LSA_SINGLE_CALL_ENUM_INFO, *PCT_LSA_SINGLE_CALL_ENUM_INFO;
+
+//
+// Sam Account Types
+//
+
+typedef enum _CT_SAM_ACCOUNT_TYPE {
+
+ CT_SAM_USER = 1,
+ CT_SAM_GROUP,
+ CT_SAM_ALIAS
+
+} CT_SAM_ACCOUNT_TYPE, *PCT_SAM_ACCOUNT_TYPE;
+
+
+#define TstAllocatePool(IgnoredPoolType,NumberOfBytes) \
+ RtlAllocateHeap(RtlProcessHeap(), 0, NumberOfBytes)
+
+#define TstDeallocatePool(Pointer) \
+ RtlFreeHeap(RtlProcessHeap(), 0, Pointer)
+
+//
+// Define the Bedrock domain and its inhabitants
+//
+// Bedrock Domain S-1-39824-21-3-17
+// Fred S-1-39824-21-3-17-2
+// Wilma S-1-39824-21-3-17-3
+// Pebbles S-1-39824-21-3-17-4
+// Dino S-1-39824-21-3-17-5
+// Barney S-1-39824-21-3-17-6
+// Betty S-1-39824-21-3-17-7
+// Bambam S-1-39824-21-3-17-8
+// Flintstone S-1-39824-21-3-17-9
+// Rubble S-1-39824-21-3-17-10
+// Adult S-1-39824-21-3-17-11
+// Child S-1-39824-21-3-17-12
+// Neanderthol S-1-39824-21-3-17-13
+//
+
+#define BEDROCK_AUTHORITY {0,0,0,0,155,144}
+
+#define BEDROCKA_AUTHORITY {0,0,0,0,155,145}
+#define BEDROCKB_AUTHORITY {0,0,0,0,155,146}
+#define BEDROCKC_AUTHORITY {0,0,0,0,155,147}
+#define BEDROCKD_AUTHORITY {0,0,0,0,155,148}
+#define BEDROCKE_AUTHORITY {0,0,0,0,155,149}
+
+#define BEDROCK_SUBAUTHORITY_0 0x00000015L
+#define BEDROCK_SUBAUTHORITY_1 0x00000003L
+#define BEDROCK_SUBAUTHORITY_2 0x00000011L
+
+#define BEDROCKA_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKA_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKA_SUBAUTHORITY_2 0x00000111L
+
+#define BEDROCKB_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKB_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKB_SUBAUTHORITY_2 0x00000211L
+
+#define BEDROCKC_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKC_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKC_SUBAUTHORITY_2 0x00000311L
+
+#define BEDROCKD_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKD_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKD_SUBAUTHORITY_2 0x00000411L
+
+#define BEDROCKE_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKE_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKE_SUBAUTHORITY_2 0x00000511L
+
+#define FRED_RID 0x00000002L
+#define WILMA_RID 0x00000003L
+#define PEBBLES_RID 0x00000004L
+#define DINO_RID 0x00000005L
+
+#define BARNEY_RID 0x00000006L
+#define BETTY_RID 0x00000007L
+#define BAMBAM_RID 0x00000008L
+
+#define FLINTSTONE_RID 0x00000009L
+#define RUBBLE_RID 0x0000000AL
+
+#define ADULT_RID 0x0000000BL
+#define CHILD_RID 0x0000000CL
+
+#define NEANDERTHOL_RID 0x0000000DL
+
+
+PSID BedrockDomainSid;
+
+PSID BedrockADomainSid;
+PSID BedrockBDomainSid;
+PSID BedrockCDomainSid;
+PSID BedrockDDomainSid;
+PSID BedrockEDomainSid;
+
+PSID FredSid;
+PSID WilmaSid;
+PSID PebblesSid;
+PSID DinoSid;
+
+PSID BarneySid;
+PSID BettySid;
+PSID BambamSid;
+
+PSID FlintstoneSid;
+PSID RubbleSid;
+
+PSID AdultSid;
+PSID ChildSid;
+
+PSID NeandertholSid;
+
+
+UNICODE_STRING BedrockDomainName;
+
+UNICODE_STRING BedrockADomainName;
+UNICODE_STRING BedrockBDomainName;
+UNICODE_STRING BedrockCDomainName;
+UNICODE_STRING BedrockDDomainName;
+UNICODE_STRING BedrockEDomainName;
+
+UNICODE_STRING FredName;
+UNICODE_STRING WilmaName;
+UNICODE_STRING PebblesName;
+UNICODE_STRING DinoName;
+
+UNICODE_STRING BarneyName;
+UNICODE_STRING BettyName;
+UNICODE_STRING BambamName;
+
+UNICODE_STRING FlintstoneName;
+UNICODE_STRING RubbleName;
+
+UNICODE_STRING AdultName;
+UNICODE_STRING ChildName;
+
+UNICODE_STRING NeandertholName;
+
+//
+// Define various constants specific to this test.
+//
+
+#define CT_PAGED_POOL ((ULONG) 0x00008000L)
+#define CT_NON_PAGED_POOL ((ULONG) 0x00003000L)
+#define CT_MIN_WORKING_SET ((ULONG) 0x00000500L)
+#define CT_MAX_WORKING_SET ((ULONG) 0x00004000L)
+
+#define CT_TRUSTED_POSIX_OFFSET ((ULONG) 0x00004444L)
+#define CT_TRUSTED_CONTROLLER_COUNT ((ULONG) 0x00000004L)
+
+#define CT_ACCOUNT_DOMAIN_RID ((ULONG) 0x00008128L)
+#define CT_PRIMARY_DOMAIN_RID (CT_ACCOUNT_DOMAIN_RID + 1)
+
+OBJECT_ATTRIBUTES ObjectAttributes;
+SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+
+QUOTA_LIMITS QuotaLimitsLsa;
+
+LSA_HANDLE PolicyHandle = NULL;
+QUOTA_LIMITS QuotaLimitsGetFred;
+QUOTA_LIMITS QuotaLimitsSetFred;
+
+POLICY_AUDIT_LOG_INFO FredPolicyAuditLogInfo;
+POLICY_AUDIT_EVENTS_INFO FredPolicyAuditEventsInfo;
+POLICY_PRIMARY_DOMAIN_INFO FredPolicyPrimaryDomainInfo;
+POLICY_PD_ACCOUNT_INFO FredPolicyPdAccountInfo;
+POLICY_ACCOUNT_DOMAIN_INFO FredPolicyAccountDomainInfo;
+POLICY_LSA_SERVER_ROLE_INFO FredPolicyLsaServerRoleInfo;
+POLICY_REPLICA_SOURCE_INFO FredPolicyReplicaSourceInfo;
+POLICY_DEFAULT_QUOTA_INFO FredPolicyDefaultQuotaInfo;
+POLICY_MODIFICATION_INFO FredPolicyModificationInfo;
+
+
+typedef struct _CT_SECRET_INFO {
+
+ UNICODE_STRING SecretName;
+ UNICODE_STRING CurrentValue;
+ UNICODE_STRING OldValue;
+ PUNICODE_STRING ReturnedCurrentValue;
+ PUNICODE_STRING ReturnedOldValue;
+
+} CT_SECRET_INFO, *PCT_SECRET_INFO;
+
+CT_SECRET_INFO SecretInfoFred;
+CT_SECRET_INFO SecretInfoWilma;
+CT_SECRET_INFO SecretInfoPebbles;
+CT_SECRET_INFO SecretInfoDino;
+CT_SECRET_INFO SecretInfoBarney;
+
+ACCESS_MASK DesiredAccessFred;
+ACCESS_MASK DesiredAccessWilma;
+ACCESS_MASK DesiredAccessPebbles;
+ACCESS_MASK DesiredAccessDino;
+ACCESS_MASK DesiredAccessBarney;
+
+
+ACCESS_MASK DesiredAccessBedrockA;
+ACCESS_MASK DesiredAccessBedrockB;
+ACCESS_MASK DesiredAccessBedrockC;
+ACCESS_MASK DesiredAccessBedrockD;
+ACCESS_MASK DesiredAccessBedrockE;
+
+LSA_HANDLE AccountHandleFred;
+LSA_HANDLE AccountHandleWilma;
+LSA_HANDLE AccountHandlePebbles;
+LSA_HANDLE AccountHandleDino;
+LSA_HANDLE AccountHandleBarney;
+
+LSA_HANDLE AccountHandleFred2;
+LSA_HANDLE AccountHandleWilma2;
+LSA_HANDLE AccountHandlePebbles2;
+LSA_HANDLE AccountHandleDino2;
+
+LSA_HANDLE AccountHandleFred3;
+
+LSA_HANDLE AccountHandleFredOpen;
+LSA_HANDLE AccountHandleWilmaOpen;
+LSA_HANDLE AccountHandlePebblesOpen;
+LSA_HANDLE AccountHandleDinoOpen;
+
+PPRIVILEGE_SET PrivilegesAddFred;
+PPRIVILEGE_SET PrivilegesEnumFred;
+PPRIVILEGE_SET PrivilegesRemoveFred;
+
+PULONG BadAddress = (PULONG) 0xefefefef;
+
+LSA_TRUST_INFORMATION TrustedDomainInfoBedrockA;
+LSA_TRUST_INFORMATION TrustedDomainInfoBedrockB;
+LSA_TRUST_INFORMATION TrustedDomainInfoBedrockC;
+LSA_TRUST_INFORMATION TrustedDomainInfoBedrockD;
+LSA_TRUST_INFORMATION TrustedDomainInfoBedrockE;
+
+
+LSA_HANDLE TrustedDomainHandleBedrockA;
+LSA_HANDLE TrustedDomainHandleBedrockB;
+LSA_HANDLE TrustedDomainHandleBedrockC;
+LSA_HANDLE TrustedDomainHandleBedrockD;
+LSA_HANDLE TrustedDomainHandleBedrockE;
+
+LSA_HANDLE TrustedDomainHandleBedrockA2;
+LSA_HANDLE TrustedDomainHandleBedrockB2;
+LSA_HANDLE TrustedDomainHandleBedrockC2;
+LSA_HANDLE TrustedDomainHandleBedrockD2;
+
+LSA_HANDLE TrustedDomainHandleBedrockA3;
+
+LSA_HANDLE TrustedDomainHandleBedrockAOpen;
+LSA_HANDLE TrustedDomainHandleBedrockBOpen;
+LSA_HANDLE TrustedDomainHandleBedrockCOpen;
+LSA_HANDLE TrustedDomainHandleBedrockDOpen;
+
+LSA_HANDLE SecretHandleFred;
+LSA_HANDLE SecretHandleWilma;
+LSA_HANDLE SecretHandlePebbles;
+LSA_HANDLE SecretHandleDino;
+LSA_HANDLE SecretHandleBarney;
+
+LSA_HANDLE SecretHandleFred2;
+LSA_HANDLE SecretHandleWilma2;
+LSA_HANDLE SecretHandlePebbles2;
+LSA_HANDLE SecretHandleDino2;
+
+LSA_HANDLE SecretHandleFred3;
+
+LSA_HANDLE SecretHandleFredOpen;
+LSA_HANDLE SecretHandleWilmaOpen;
+LSA_HANDLE SecretHandlePebblesOpen;
+LSA_HANDLE SecretHandleDinoOpen;
+
+int Level;
+
+BOOLEAN SidFound;
+ULONG EnumNumber;
+ULONG Base, Index, SearchIndex;
+ULONG CountReturned;
+
+PVOID EnumerationInformation;
+ULONG EnumerationContext;
+ULONG PreferedMaximumLength;
+
+PUNICODE_STRING SystemName = NULL;
+
+typedef struct _CT_LSA_ACCOUNT_SID_INFO {
+
+ PSID Sid;
+ BOOLEAN SidFound;
+
+} CT_LSA_ACCOUNT_SID_INFO;
+
+CT_LSA_ACCOUNT_SID_INFO AccountSidInfo[4];
+
+
+typedef struct _CT_LSA_TRUSTED_DOMAIN_SID_INFO {
+
+ PSID Sid;
+ BOOLEAN SidFound;
+
+} CT_LSA_TRUSTED_DOMAIN_SID_INFO;
+
+CT_LSA_TRUSTED_DOMAIN_SID_INFO TrustedDomainSidInfo[4];
+
+
+typedef struct _CT_LSA_SECRET_NAME_INFO {
+
+ UNICODE_STRING Name;
+ BOOLEAN NameFound;
+
+} CT_LSA_SECRET_NAME_INFO;
+
+CT_LSA_SECRET_NAME_INFO SecretNameInfo[4];
+
+//
+// Main is just a wrapper for the main test routines
+//
+
+//
+// Globally Visible Table of Sids.
+//
+
+PSID AccountDomainSid = NULL;
+PSID PrimaryDomainSid = NULL;
+PSID *TrustedDomainSids = NULL;
+
+BOOLEAN
+CtLsaVariableInitialization();
+
+BOOLEAN
+CtLsaPolicyObject(
+ IN BOOLEAN TrustedClient
+ );
+
+BOOLEAN
+CtLsaPolicyOpenClose(
+ );
+
+BOOLEAN
+CtLsaPolicySetQueryInfo(
+ );
+
+BOOLEAN
+CtLsaPolicySerialNumber(
+ IN BOOLEAN TrustedClient
+ );
+
+BOOLEAN
+CtLsaPolicySetQuerySub(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PVOID PolicyInformation
+ );
+
+BOOLEAN
+CtLsaPolicyInfoClassCompare(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PVOID PolicyInformation1,
+ IN PVOID PolicyInformation2
+ );
+
+BOOLEAN
+CtLsaPolicyQueryInfoAllowed(
+ IN POLICY_INFORMATION_CLASS InformationClass
+ );
+
+BOOLEAN
+CtLsaPolicySetInfoAllowed(
+ IN POLICY_INFORMATION_CLASS InformationClass
+ );
+
+ULONG
+CtLsaPolicyInfoClassSize(
+ IN POLICY_INFORMATION_CLASS InformationClass
+ );
+
+BOOLEAN
+CtLsaPolicyAuditLogInfo();
+
+BOOLEAN
+CtLsaPolicyAuditEventsInfo();
+
+BOOLEAN
+CtLsaPolicyPrimaryDomainInfo();
+
+BOOLEAN
+CtLsaPolicyPdAccountInfo();
+
+BOOLEAN
+CtLsaPolicyAccountDomainInfo();
+
+BOOLEAN
+CtLsaPolicyLsaServerRoleInfo();
+
+BOOLEAN
+CtLsaPolicyReplicaSourceInfo();
+
+BOOLEAN
+CtLsaPolicyDefaultQuotaInfo();
+
+BOOLEAN
+CtLsaPolicyModificationInfo();
+
+BOOLEAN
+CtLsaAccountObject(
+ IN BOOLEAN TrustedClient
+ );
+
+BOOLEAN
+CtLsaAccountCreate(
+ );
+
+BOOLEAN
+CtLsaAccountOpenClose(
+ );
+
+BOOLEAN
+CtLsaAccountPrivileges(
+ );
+
+BOOLEAN
+CtLsaAccountQuotaLimits(
+ );
+
+BOOLEAN
+CtLsaAccountSystemAccess(
+ );
+
+BOOLEAN
+CtLsaAccountEnumeration(
+ );
+
+BOOLEAN
+CtLsaAccountDelete(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainObject(
+ IN BOOLEAN TrustedClient
+ );
+
+BOOLEAN
+CtLsaTrustedDomainCreate(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainOpenClose(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainSetQueryInfo(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainAccountInfo(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainControllersInfo(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainPosixOffsetInfo(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainSetQuerySub(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID TrustedDomainInformation
+ );
+
+BOOLEAN
+CtLsaTrustedDomainInfoClassCompare(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID TrustedDomainInformation1,
+ IN PVOID TrustedDomainInformation2
+ );
+
+BOOLEAN
+CtLsaTrustedDomainQueryInfoAllowed(
+ IN TRUSTED_INFORMATION_CLASS InformationClass
+ );
+
+BOOLEAN
+CtLsaTrustedDomainSetInfoAllowed(
+ IN TRUSTED_INFORMATION_CLASS InformationClass
+ );
+
+BOOLEAN
+CtLsaTrustedDomainEnumeration(
+ );
+
+BOOLEAN
+CtLsaTrustedDomainDelete(
+ );
+
+VOID
+CtLsaTrustedDomainSetInfo(
+ IN PSID DomainSid,
+ IN PUNICODE_STRING DomainName,
+ OUT PLSA_TRUST_INFORMATION TrustedDomainInfo
+ );
+
+BOOLEAN
+CtLsaSecretObject(
+ IN BOOLEAN TrustedClient
+ );
+
+BOOLEAN
+CtLsaSecretCreate(
+ );
+
+BOOLEAN
+CtLsaSecretOpenClose(
+ );
+
+BOOLEAN
+CtLsaSecretSetQueryValue(
+ );
+
+BOOLEAN
+CtLsaSecretEnumeration(
+ );
+
+BOOLEAN
+CtLsaSecretSetTimes(
+ );
+
+BOOLEAN
+CtLsaSecretDelete(
+ );
+
+BOOLEAN
+CtLsaSecretCleanup(
+ );
+
+NTSTATUS
+CtSecretSetInfo(
+ IN PUCHAR SecretNameText,
+ IN PUCHAR CurrentValueText,
+ IN PUCHAR OldValueText,
+ OUT PCT_SECRET_INFO SecretInformation
+ );
+
+BOOLEAN
+CtLsaGeneralAPI(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ );
+
+BOOLEAN
+CtLsaLookupSids(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ );
+
+BOOLEAN
+CtLsaLookupSidsInSamDomain(
+ IN OPTIONAL PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING DomainControllerName,
+ IN PUNICODE_STRING SamDomainName,
+ IN CT_SAM_ACCOUNT_TYPE SamAccountType
+ );
+
+BOOLEAN
+CtLsaLookupNames(
+ IN PUNICODE_STRING WorkstationName
+ );
+
+BOOLEAN
+CtLsaLookupNamesInSamDomain(
+ IN OPTIONAL PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING DomainControllerName,
+ IN PUNICODE_STRING SamDomainName,
+ IN CT_SAM_ACCOUNT_TYPE SamAccountType
+ );
+
+BOOLEAN
+CtLsaLookupConfigure(
+ IN OPTIONAL PUNICODE_STRING WorkstationName,
+ IN OPTIONAL PUNICODE_STRING PrimaryDomainName,
+ IN PSID PrimaryDomainSid,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount,
+ IN OPTIONAL PUNICODE_STRING TrustedDomainNames,
+ IN PSID *TrustedDomainSids,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN ULONG TrustedDomainCtrlrTotal,
+ IN PULONG TrustedDomainCtrlrCounts
+ );
+
+BOOLEAN
+CtLsaLookupConfigureWksta(
+ IN PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING PrimaryDomainName,
+ IN PUNICODE_STRING PrimaryDomainSid,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount
+ );
+
+BOOLEAN
+CtLsaLookupConfigurePDC(
+ IN PUNICODE_STRING PrimaryDomainName,
+ IN PSID PrimaryDomainSid,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount,
+ IN PUNICODE_STRING TrustedDomainNames,
+ IN PSID *TrustedDomainSids,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN ULONG TrustedDomainCtrlrTotal,
+ IN PULONG TrustedDomainCtrlrCounts
+ );
+
+BOOLEAN
+CtLsaGeneraLookupConfigureTDC(
+ IN PUNICODE_STRING TrustedDomainNames,
+ IN PSID *TrustedDomainSids,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN ULONG TrustedDomainCtrlrTotal,
+ IN PULONG TrustedDomainCtrlrCounts
+ );
+
+NTSTATUS
+CtLsaLookupConfigureTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN PLSA_TRUST_INFORMATION TrustedDomainInformation,
+ IN PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo
+ );
+
+BOOLEAN
+CtLsaLookupPrintConfiguration(
+ IN PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING PrimaryDomainName,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount,
+ IN PUNICODE_STRING TrustedDomainNames,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN PULONG TrustedDomainCtrlrCounts
+ );
+
+BOOLEAN
+CtLsaGeneralConstructSids(
+ OUT PSID *AccountDomainSid,
+ OUT PSID *PrimaryDomainSid,
+ OUT PSID **TrustedDomainSids,
+ IN ULONG TrustedDomainCount
+ );
+
+BOOLEAN
+CtLsaGeneralSetQuerySecurityObject(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ );
+
+BOOLEAN
+CtLsaGeneralEnumeratePrivileges(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ );
+
+BOOLEAN
+CtLsaGeneralClearAuditLog(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ );
+
+NTSTATUS
+CtLsaSetQuerySecurityObjectSub(
+ IN LSA_HANDLE ObjectHandle,
+ IN PSID NewOwnerSid
+ );
+
+BOOLEAN
+CtLsaGeneralInitUnknownSid(
+ IN PSID Sid,
+ OUT PCT_UNKNOWN_SID_ENTRY UnknownSidInfo,
+ IN BOOLEAN DomainKnown
+ );
+
+VOID
+CtLsaGeneralInitUnknownName(
+ IN PUNICODE_STRING Name,
+ IN OPTIONAL PUNICODE_STRING DomainName,
+ IN OPTIONAL PSID DomainSid,
+ OUT PCT_UNKNOWN_NAME_ENTRY UnknownNameInfo,
+ IN BOOLEAN DomainKnown
+ );
+
+NTSTATUS
+CtLsaGeneralSidToLogicalNameObject(
+ IN PSID Sid,
+ OUT PUNICODE_STRING LogicalName
+ );
+
+BOOLEAN
+CtLsaGeneralVerifyTrustInfo(
+ IN PLSA_TRUST_INFORMATION TrustInformation,
+ IN PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSidEntry
+ );
+
+BOOLEAN
+CtLsaGeneralBuildSid(
+ PSID *Sid,
+ PSID DomainSid,
+ ULONG RelativeId
+ );
+
+VOID
+CtLsaInitObjectAttributes(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService
+ );
+
+BOOLEAN
+CtEqualQuotaLimits(
+ IN PQUOTA_LIMITS QuotaLimits1,
+ IN PQUOTA_LIMITS QuotaLimits2
+ );
+
+NTSTATUS
+CtRtlConvertSidToUnicodeString(
+ IN PSID Sid,
+ OUT PUNICODE_STRING UnicodeString
+ );
+
+VOID
+CtLsaUsage(
+ );
+
+VOID
+lsassmain(
+ );
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// LSA Component Test for RPC API - main program //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+#define CT_MAX_CONTROLLERS ((ULONG) 0x00000080)
+#define CT_MAX_PRIMARY_DOMAIN_CTRLRS ((ULONG) 0x00000020)
+#define CT_MAX_TRUSTED_DOMAINS ((ULONG) 0x00000020)
+#define CT_MAX_TRUSTED_DOMAIN_CTRLRS ((ULONG) 0x00000020)
+
+VOID _CRTAPI1
+main (argc, argv)
+int argc;
+char **argv;
+
+{
+ int Index;
+ int IterationCount, Count;
+ ULONG LongCount;
+ BOOLEAN Forever;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN BooleanStatus = TRUE;
+ ANSI_STRING WorkstationNameAnsi;
+ ANSI_STRING TrustedDomainNameAnsi;
+ ANSI_STRING TrustedDomainCtrlrNameAnsi;
+ UNICODE_STRING WorkstationName;
+ UNICODE_STRING PrimaryDomainCtrlrNames[CT_MAX_PRIMARY_DOMAIN_CTRLRS];
+ UNICODE_STRING TrustedDomainNames[CT_MAX_TRUSTED_DOMAINS];
+ UNICODE_STRING TrustedDomainCtrlrNames[CT_MAX_TRUSTED_DOMAIN_CTRLRS];
+ BOOLEAN WorkstationSpecified = FALSE;
+ BOOLEAN PrimaryDomainSpecified = FALSE;
+ BOOLEAN TrustedDomainsSpecified = FALSE;
+ BOOLEAN SamDatabasesAlreadyLoaded = FALSE;
+ ULONG PrimaryDomainCtrlrCount;
+ ULONG TrustedDomainCount;
+ ULONG TrustedDomainCtrlrTotal;
+ ULONG TrustedDomainCtrlrCounts[CT_MAX_TRUSTED_DOMAINS];
+ BOOLEAN TrustedDomainControllerKeywordExpected = FALSE;
+ BOOLEAN ConfigureSystem = FALSE;
+ BOOLEAN RunAllTests = FALSE;
+ BOOLEAN RunGeneralApiTests = FALSE;
+ BOOLEAN RunLookupApiTests = FALSE;
+ BOOLEAN RunPolicyObjectApiTests = FALSE;
+ BOOLEAN RunAccountObjectApiTests = FALSE;
+ BOOLEAN RunTrustedDomainObjectApiTests = FALSE;
+ BOOLEAN RunSecretObjectApiTests = FALSE;
+ BOOLEAN TrustedClient = FALSE;
+ BOOLEAN RunLsaInit = FALSE;
+
+ RtlZeroMemory( &WorkstationName,sizeof(UNICODE_STRING) );
+
+ RtlZeroMemory(
+ PrimaryDomainCtrlrNames,
+ sizeof(UNICODE_STRING) * CT_MAX_PRIMARY_DOMAIN_CTRLRS
+ );
+
+ RtlZeroMemory(
+ TrustedDomainNames,
+ sizeof(UNICODE_STRING) * CT_MAX_TRUSTED_DOMAINS
+ );
+
+ RtlZeroMemory(
+ TrustedDomainCtrlrNames,
+ sizeof(UNICODE_STRING) * CT_MAX_TRUSTED_DOMAIN_CTRLRS
+ );
+
+ RtlZeroMemory(
+ TrustedDomainCtrlrCounts,
+ sizeof(ULONG) * CT_MAX_TRUSTED_DOMAINS
+ );
+
+ if (argc < 1) {
+
+ CtLsaUsage();
+ return;
+ }
+
+ //
+ // Parse the parameters (if any). Assume that a parameter beginning
+ // \\ is the server name and a parameter beginning -l is the level
+ //
+
+ Level = 1;
+
+ IterationCount = 0;
+ PrimaryDomainCtrlrCount = 0;
+ TrustedDomainCount = 0;
+ TrustedDomainCtrlrTotal = 0;
+
+ if (argc >= 2) {
+
+ for(Index = 1; Index < argc; Index++) {
+
+ if (strncmp(argv[Index], "-level", 6) == 0) {
+
+ Level = atoi(argv[Index]+2);
+
+ if ((Level < 1) || (Level > 2)) {
+
+ DbgPrint("Level not 1 or 2\n");
+ DbgPrint("Test abandoned\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ } else if (strncmp(argv[Index], "-cfg", 4) == 0) {
+
+ ConfigureSystem = TRUE;
+
+ } else if (strncmp(argv[Index], "-c", 2) == 0) {
+
+ IterationCount = atoi(argv[Index]+2);
+
+ if (IterationCount < 0) {
+
+ DbgPrint("Iteration Count < 0\n");
+ DbgPrint("Test abandoned\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ } else if (strncmp(argv[Index], "-wks", 4) == 0) {
+
+ //
+ // The Workstation Name keyword has been specified. Save
+ // its name for LSA Configuration.
+ //
+
+ if (Index + 1 >= argc) {
+
+ DbgPrint("Workstation name missing\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ Index++;
+
+ if (strncmp(argv[Index], "-", 1) == 0) {
+
+ continue;
+ }
+
+ if (strncmp(argv[Index], "\\\\", 2) != 0) {
+
+ DbgPrint(".. Invalid workstation name\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ //
+ // A workstation name is specified. Construct a Unicode
+ // String containing the specified name.
+ //
+
+ WorkstationSpecified = TRUE;
+
+ RtlInitString( &WorkstationNameAnsi, argv[Index] );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &WorkstationName,
+ &WorkstationNameAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Failed building Workstation Name\n"
+ "... RtlAnsiStringToUnicodeString returned 0x%lx\n",
+ Status
+ );
+ DbgPrint("LSA RPC CT - Whole test abandoned\n");
+ break;
+ }
+
+ } else if (strncmp(argv[Index], "-pdc", 4) == 0) {
+
+ //
+ // The Primary Domain Controller Keyword has been specified.
+ //
+
+ if (Index + 1 >= argc) {
+
+ DbgPrint("No Primary Domain Controllers given\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ Index++;
+
+ if (strncmp(argv[Index], "-", 1) == 0) {
+
+ DbgPrint("No Primary Domain Controllers given\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ //
+ // One or more Primary Domain Controller names have been specified.
+ // Since the Primary Domain is also trusted, save the names
+ // in the Trusted Domain Names array.
+ //
+
+ PrimaryDomainCtrlrCount = 0;
+
+ while ((Index < argc) && strncmp(argv[Index], "-", 1) != 0) {
+
+ if (strncmp(argv[Index], "\\\\", 2) != 0) {
+
+ DbgPrint(".. Invalid Primary Domain Ctrlr Name\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ RtlInitString( &TrustedDomainCtrlrNameAnsi, argv[Index] );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &TrustedDomainCtrlrNames[ TrustedDomainCtrlrTotal ],
+ &TrustedDomainCtrlrNameAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - failed 0x%lx building Primary", Status);
+ DbgPrint(" Domain Controller Name\n");
+ DbgPrint("LSA RPC CT - Whole test abandoned\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ PrimaryDomainCtrlrCount++;
+ TrustedDomainCtrlrTotal++;
+ Index++;
+ }
+
+ Index--;
+
+ TrustedDomainCtrlrCounts[0] = PrimaryDomainCtrlrCount;
+
+ } else if (strncmp(argv[Index], "-pd", 3) == 0) {
+
+ //
+ // The Primary Domain Name Keyword has been specified.
+ //
+
+ if (Index + 1 >= argc) {
+
+ DbgPrint("Primary Domain name missing\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ Index++;
+
+ if (strncmp(argv[Index], "-", 1) == 0) {
+
+ continue;
+ }
+
+ //
+ // A Primary Domain name has been specified. Save the name.
+ // Also, save the name in slot 0 of the TrustedDomainNames
+ // array.
+ //
+
+ PrimaryDomainSpecified = TRUE;
+
+ RtlInitString( &TrustedDomainNameAnsi, argv[Index] );
+ Status = RtlAnsiStringToUnicodeString(
+ TrustedDomainNames,
+ &TrustedDomainNameAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - failed 0x%lx building Primary", Status);
+ DbgPrint(" Domain Name\n");
+ DbgPrint("LSA RPC CT - Whole test abandoned\n");
+ break;
+ }
+
+ TrustedDomainCount++;
+
+ } else if (strncmp(argv[Index], "-tdc", 4) == 0) {
+
+ //
+ // A Trusted Domain Controller Keyword has been specified.
+ // Verify that one is expected.
+ //
+
+ if (!TrustedDomainControllerKeywordExpected) {
+
+ BooleanStatus = FALSE;
+ DbgPrint(
+ "-tdc keyword specified when not expected"
+ );
+ }
+
+ TrustedDomainControllerKeywordExpected = FALSE;
+
+ if (Index + 1 >= argc) {
+
+ DbgPrint("No Trusted Domain Controllers given\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ Index++;
+
+ if (strncmp(argv[Index], "-", 1) == 0) {
+
+ DbgPrint("No Trusted Domain Controllers given\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ //
+ // One or more Trusted Domain Controller names have been specified.
+ // Save the names.
+ //
+
+ while ((Index < argc) && strncmp(argv[Index], "-", 1) != 0) {
+
+ if (strncmp(argv[Index], "\\\\", 2) != 0) {
+
+ DbgPrint(".. Invalid Trusted Domain Ctrlr Name\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ RtlInitString( &TrustedDomainCtrlrNameAnsi, argv[Index] );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &TrustedDomainCtrlrNames[ TrustedDomainCtrlrTotal ],
+ &TrustedDomainCtrlrNameAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - failed 0x%lx building Trusted"
+ " Domain Controller Name\n"
+ "LSA RPC CT - Whole test abandoned\n",
+ Status
+ );
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ TrustedDomainCtrlrTotal++;
+ TrustedDomainCtrlrCounts[TrustedDomainCount - 1]++;
+ Index++;
+ }
+
+ Index--;
+
+ } else if (strncmp(argv[Index], "-td", 3) == 0) {
+
+ //
+ // A Trusted Domain Name Keyword has been specified.
+ // One of these has to be specified for each Trusted Domain
+ // since each -td <TrustedDomainName> must be followed
+ // by -tdc [\\<TrustedDomainCtrlrName>]+
+ //
+
+ //
+ // Verify that the Primary Domain has already been specified.
+ // This is a Trusted Domain and it must be specified
+ // first. The PrimaryDomainSpecified flag should be set
+ // and the TrustedDomainCount should be 1.
+ //
+
+ if ((!PrimaryDomainSpecified) || TrustedDomainCount != 1) {
+
+ DbgPrint(
+ "Primary Domain must be specified before\n"
+ "Trusted Domains"
+ );
+
+ BooleanStatus = FALSE;
+ break;
+ }
+
+
+ if (Index + 1 >= argc) {
+
+ DbgPrint("Trusted Domain name missing\n");
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ Index++;
+
+ if (strncmp(argv[Index], "-", 1) == 0) {
+
+ continue;
+ }
+
+ //
+ // A Trusted Domain Name is expected. Save it in the
+ // next available Slot in the TrustedDomainNames array.
+ // Note that Slot 0 is always reserved for the Primary
+ // Domain Name (the Primary Domain is always trusted by
+ // itself).
+ //
+
+ RtlInitString( &TrustedDomainNameAnsi, argv[Index] );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &TrustedDomainNames[ TrustedDomainCount ],
+ &TrustedDomainNameAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - failed 0x%lx building Trusted"
+ " Domain Controller Name\n"
+ "LSA RPC CT - Whole test abandoned\n",
+ Status
+ );
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ TrustedDomainCount++;
+ TrustedDomainsSpecified = TRUE;
+ TrustedDomainControllerKeywordExpected = TRUE;
+
+ } else if (strncmp(argv[Index], "-sdb", 4) == 0) {
+
+ SamDatabasesAlreadyLoaded = TRUE;
+
+ } else if (strncmp(argv[Index], "-all", 4) == 0) {
+
+ RunAllTests = TRUE;
+
+ } else if (strncmp(argv[Index], "-general", 8) == 0) {
+
+ RunGeneralApiTests = TRUE;
+
+ } else if (strncmp(argv[Index], "-policy", 7) == 0) {
+
+ RunPolicyObjectApiTests = TRUE;
+
+ } else if (strncmp(argv[Index], "-account", 8) == 0) {
+
+ RunAccountObjectApiTests = TRUE;
+
+ } else if (strncmp(argv[Index], "-domain", 7) == 0) {
+
+ RunTrustedDomainObjectApiTests = TRUE;
+
+ } else if (strncmp(argv[Index], "-secret", 7) == 0) {
+
+ RunSecretObjectApiTests = TRUE;
+
+ } else if (strncmp(argv[Index], "-lookup", 7) == 0) {
+
+ RunLookupApiTests = TRUE;
+
+ } else if (strncmp(argv[Index], "-lsainit", 8) == 0) {
+
+ RunLsaInit = TRUE;
+ TrustedClient = TRUE;
+
+ } else {
+
+ CtLsaUsage();
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+ }
+
+ //
+ // If the parameter validation was unsuccessful, quit.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto MainError;
+ }
+
+ //
+ // ctlsarpc command parameters have been validated. Before running
+ // tests, do normal LSA initialization if requested. This is run
+ // if and only if we are substituting for the LSA process (lsass.exe).
+ //
+
+ if (RunLsaInit) {
+
+ lsassmain();
+ }
+
+ //
+ // Initialize the Well Known Sids table.
+ //
+
+ if (!LsaIInitializeWellKnownSids( &WellKnownSids )) {
+
+ return;
+ }
+
+ //
+ // Construct a Table of Sids for allocation as the Account Domain,
+ // Primary Domain and other Trusted Domain Sids..
+ //
+
+ if (!CtLsaGeneralConstructSids(
+ &AccountDomainSid,
+ &PrimaryDomainSid,
+ &TrustedDomainSids,
+ TrustedDomainCount
+ )) {
+
+ goto MainError;
+ }
+
+ //
+ // Perform Configuration for the Lookup tests. Note that the
+ // Primary Domain Name and Controllers appear first in the
+ // TrustedDomainNames and TrustedDomainCtrlrNames arrays.
+ // The Trusted Domain Count includes 1 for the Primary Domain.
+ // The Trusted Domain Ctrlr Count includes the Primary Domain
+ // Ctrlr Count.
+ //
+
+ if (ConfigureSystem) {
+
+ if (!CtLsaLookupConfigure(
+ &WorkstationName,
+ &TrustedDomainNames[0],
+ PrimaryDomainSid,
+ &TrustedDomainCtrlrNames[0],
+ PrimaryDomainCtrlrCount,
+ TrustedDomainNames,
+ TrustedDomainSids,
+ TrustedDomainCount,
+ TrustedDomainCtrlrNames,
+ TrustedDomainCtrlrTotal,
+ TrustedDomainCtrlrCounts
+ )) {
+
+ goto MainError;
+ }
+
+
+ //
+ // If sdb flag not specified, remind caller to run bldsam2 on machines
+ // being configured.
+ //
+
+ if (!SamDatabasesAlreadyLoaded) {
+
+ DbgPrint(
+ "LSA RPC CT: Configuration for Lookup Sid/name tests complete\n"
+ "The -sdb flag was not specified - You must now run bldsam2\n"
+ "to load the SAM Databases for each machine involved\n"
+ );
+
+ goto MainFinish;
+ }
+ }
+
+ DbgPrint("LSA RPC CT - Test Begins\n");
+
+
+ //
+ // Setup test domains etc.
+ //
+
+ if (!CtLsaVariableInitialization()) {
+
+ DbgPrint("LSA RPC CT - Test Variable Initialization failed\n");
+ DbgPrint("Test abandoned\n");
+ return;
+ }
+
+
+ Forever = FALSE;
+
+ if (IterationCount == 0) {
+
+ IterationCount = 1;
+ Forever = TRUE;
+ }
+
+ for (Count = 0, LongCount =1; Count < IterationCount; Count++, LongCount++) {
+
+ if (LongCount == 0xffffffff) {
+
+ LongCount = 0;
+ }
+
+ DbgPrint("Iteration %d begins\n", LongCount);
+
+ if (RunAllTests || RunGeneralApiTests) {
+
+ if (!CtLsaGeneralAPI( &WorkstationName )) {
+
+ DbgPrint( "LSA RPC CT - General API test failed\n" );
+ }
+ }
+
+ if (RunLookupApiTests) {
+
+ if (!CtLsaLookupSids( &WorkstationName )) {
+
+ DbgPrint( "LSA RPC CT - Lookup Sids Tests failed\n" );
+ }
+
+ if (!CtLsaLookupNames( &WorkstationName )) {
+
+ DbgPrint( "LSA RPC CT - Lookup Names Tests failed\n" );
+ }
+ }
+
+ if (RunAllTests || RunPolicyObjectApiTests) {
+
+ if (!CtLsaPolicyObject(TrustedClient)) {
+
+ DbgPrint("LSA RPC CT - Policy Object API test failed\n");
+ }
+ }
+
+ if (RunAllTests || RunAccountObjectApiTests) {
+
+ if (!CtLsaAccountObject(TrustedClient)) {
+
+ DbgPrint("LSA RPC CT - Account Object API test failed\n");
+ }
+ }
+
+ if (RunAllTests || RunTrustedDomainObjectApiTests) {
+
+ if (!CtLsaTrustedDomainObject(TrustedClient)) {
+
+ DbgPrint(
+ "LSA RPC CT - Trusted Domain Object API test failed\n"
+ );
+ }
+ }
+
+ if (RunAllTests || RunSecretObjectApiTests) {
+
+ if (!CtLsaSecretObject(TrustedClient)) {
+
+ DbgPrint( "LSA RPC CT - Secret Object API test failed\n" );
+ }
+ }
+
+ if (Forever) {
+
+ Count = 0;
+ }
+ }
+
+ DbgPrint("LSA RPC CT - Test Ends\n");
+
+MainFinish:
+
+ return;
+
+MainError:
+
+ goto MainFinish;
+}
+
+
+VOID
+CtLsaUsage(
+ )
+
+/*++
+
+Routine Description:
+
+ This function prints out the usage of the ctlsarpc command.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None
+
+--*/
+
+{
+ DbgPrint(
+ "Usage: ctlsarpc [-level<Level]\n"
+ " [-c<IterationCount>]\n"
+ " [-policy][-account][-domain][-secret][-all]\n"
+ " [-cfg]\n"
+ " [-wks \\<WorkstationName> ]\n"
+ " [-pd <PrimaryDomainName> ]\n"
+ );
+
+ DbgPrint(
+ " [-pdc [\\<PDCName>]+]\n"
+ " [-td <TrustedDomainName>]\n"
+ " [-tdc [\\<TDCName>]+]\n"
+ " [-sdb]\n\n"
+ );
+ DbgPrint(
+ "where <Level> = 1 for normal test\n"
+ " <Level> = 2 for full test with exception cases\n"
+ " <IterationCount = iteration count (0 = forever)\n"
+ " -policy = Run Policy Object Tests\n"
+ " -account = Run Account Object Tests\n"
+ );
+
+ DbgPrint(
+ " -domain = Run TrustedDomain Object Tests\n"
+ " -secret = Run Secret Object Tests\n"
+ " -general = Run General non-object Specific Tests\n"
+ " -lookup = Run Lookup Sids/Names tests\n"
+ " -all = Run all of the above Tests (except Lookup Sids/Names\n"
+ );
+
+ DbgPrint(
+ " -cfg = Configure for Lookup Sid/Name tests\n"
+ " <WorkstationName> = name of workstation to\n"
+ " be configured for Sid/Name Lookup tests\n"
+ " <PrimaryDomainName> = name of Workstation's\n"
+ " Primary Domain\n"
+ );
+
+ DbgPrint(
+ " <PDCName> specifies a Primary Domain Controller\n"
+ " <TrustedDomainName> = name of a Domain that is\n"
+ " trusted for authentication by the PD\n"
+ " <TDCName> sspecifies a Trusted Domain Controller\n"
+ );
+
+ DbgPrint(
+ " -lsainit - Specifies that LSA initialization is to\n"
+ " be run. This is necessary for all tests involving\n"
+ " LsaIxx Api called by trusted callers linked to\n"
+ " the Security Process. If omitted, these tests\n"
+ " are skipped.\n"
+ );
+}
+
+
+BOOLEAN
+CtLsaPolicyObject(
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the Lsa Policy Object API.
+
+Arguments:
+
+ TrustedClient - Specifies whether Trusted Client variations
+ are to be run additionally. NOTE: These can only be run
+ with ctlsarpc -lsainit... substituted for lsass.exe
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ LSA_HANDLE PolicyHandle = NULL;
+
+ DbgPrint("********************************************************\n");
+ DbgPrint("LSA RPC CT - Test Policy Object API\n");
+ DbgPrint("********************************************************\n");
+
+ if (!CtLsaPolicySerialNumber( TrustedClient )) {
+
+ DbgPrint("LSA RPC CT - Policy Serial Number test failed\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!CtLsaPolicyOpenClose()) {
+
+ DbgPrint("LSA RPC CT - Policy Open and Close test failed\n");
+ goto PolicyObjectError;
+ }
+
+ if (!CtLsaPolicySetQueryInfo()) {
+
+ DbgPrint("LSA RPC CT - Policy Set Query Info test failed\n");
+ BooleanStatus = FALSE;
+ }
+
+PolicyObjectFinish:
+
+ return ( BooleanStatus );
+
+PolicyObjectError:
+
+ BooleanStatus = FALSE;
+ goto PolicyObjectFinish;
+}
+
+
+BOOLEAN
+CtLsaPolicyOpenClose(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the opening and closing of the Policy Object
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandle[5];
+ ULONG Index;
+
+ DbgPrint("[1] - Test Policy Open and Close API\n");
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ //
+ // Open 5 handles to the Policy Object
+ //
+
+ for (Index = 0; Index < 5; Index++) {
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle[Index]
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle %d failed 0x%lx\n",
+ Index,
+ Status
+ );
+ return FALSE;
+ }
+ }
+
+ //
+ // Close the 5 handles to the Policy Object just opened.
+ //
+
+ for (Index = 0; Index < 5; Index++) {
+
+ Status = LsaClose( PolicyHandle[Index] );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object close handle %d failed 0x%lx\n",
+ Index,
+ Status
+ );
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+CtLsaPolicySetQueryInfo(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaSetInformationPolicy and LsaQueryInformationPolicy
+ API.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ BOOLEAN - TRUE if test is successful, else FALSE
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("[2] - Test Set and Query Info API on Policy Object\n");
+
+ BooleanStatus &= CtLsaPolicyAuditLogInfo();
+ BooleanStatus &= CtLsaPolicyAuditEventsInfo();
+ BooleanStatus &= CtLsaPolicyPrimaryDomainInfo();
+ BooleanStatus &= CtLsaPolicyPdAccountInfo();
+ BooleanStatus &= CtLsaPolicyAccountDomainInfo();
+ BooleanStatus &= CtLsaPolicyLsaServerRoleInfo();
+ BooleanStatus &= CtLsaPolicyReplicaSourceInfo();
+ BooleanStatus &= CtLsaPolicyDefaultQuotaInfo();
+ BooleanStatus &= CtLsaPolicyModificationInfo();
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicySerialNumber(
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaISetSerialNumberPolicy() and
+ LsaIGetSerialNumberPolicy() API. These are available to
+ Trusted Clients only.
+
+Arguments:
+
+ TrustedClient - TRUE if client is trusted, else FALSE.
+
+Return Values:
+
+ BOOLEAN - TRUE if test is successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ LSAPR_HANDLE TrustedPolicyHandle = NULL;
+ LARGE_INTEGER SetModifiedCount, ReturnedModifiedCount;
+ LARGE_INTEGER SetCreationTime, ReturnedCreationTime;
+
+ DbgPrint("[3] - Test Set and Query Serial Number API on Policy Object\n");
+
+ if (!TrustedClient) {
+
+ DbgPrint("... test omitted because client is not trusted\n");
+ goto PolicySerialNumberFinish;
+ }
+
+ //
+ // First open a Trusted Handle to the LSA.
+ //
+
+ Status = LsaIOpenPolicyTrusted( &TrustedPolicyHandle );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Get/Set Policy Serial Number Test failed\n"
+ "LsaIOpenPolicyTrusted returned 0x%lx\n",
+ Status
+ );
+
+ goto PolicySerialNumberError;
+ }
+
+ //
+ // Setup Database Creation Time and Serial Number.
+ //
+
+ Status = NtQuerySystemTime(&SetCreationTime);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Get/Set Policy Serial Number Test failed\n"
+ "NtQuerySystemTime returned 0x%lx\n",
+ Status
+ );
+
+ goto PolicySerialNumberError;
+ }
+
+ ReturnedCreationTime = RtlConvertUlongToLargeInteger ( 0 );
+ SetModifiedCount = RtlConvertUlongToLargeInteger ( 0x00046656L );
+ ReturnedModifiedCount = RtlConvertUlongToLargeInteger ( 0 );
+
+ //
+ // Set the Policy Serial Number to a hardwired value.
+ //
+
+ Status = LsaISetSerialNumberPolicy(
+ TrustedPolicyHandle,
+ &SetModifiedCount,
+ &SetCreationTime,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Get/Set Policy Serial Number Test failed\n"
+ "LsaISetSerialNumberPolicy returned 0x%lx\n",
+ Status
+ );
+
+ goto PolicySerialNumberError;
+ }
+
+ //
+ // Now Query the Policy Serial Number just set.
+ //
+
+ Status = LsaIGetSerialNumberPolicy(
+ TrustedPolicyHandle,
+ &ReturnedModifiedCount,
+ &ReturnedCreationTime
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Get/Set Policy Serial Number Test failed\n"
+ "LsaIGetSerialNumberPolicy returned 0x%lx\n",
+ Status
+ );
+
+ goto PolicySerialNumberError;
+ }
+
+ //
+ // Now compare the ModifiedCount value returned with that set.
+ //
+
+ if (!( SetModifiedCount.QuadPart == ReturnedModifiedCount.QuadPart )) {
+
+ DbgPrint(
+ "LSA RPC CT - Get/Set Policy Serial Number Test failed\n"
+ "mismatch on ModifiedCount\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now compare the CreationTime value returned with that set.
+ //
+
+ if (!( SetCreationTime.QuadPart == ReturnedCreationTime.QuadPart )) {
+
+ DbgPrint(
+ "LSA RPC CT - Get/Set Policy Serial Number Test failed\n"
+ "mismatch on CreationTime\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto PolicySerialNumberError;
+ }
+
+PolicySerialNumberFinish:
+
+ //
+ // If necessary, close the TrustedPolicyHandle.
+ //
+
+ if (TrustedPolicyHandle != NULL) {
+
+ Status = LsarClose( &TrustedPolicyHandle );
+
+ TrustedPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Get/Set Policy Serial Number failed\n"
+ "LsaClose on TrustedPolicyHandle returned 0x%lx\n",
+ Status
+ );
+
+ goto PolicySerialNumberError;
+ }
+ }
+
+ return(BooleanStatus);
+
+PolicySerialNumberError:
+
+ BooleanStatus = FALSE;
+ goto PolicySerialNumberFinish;
+}
+
+
+BOOLEAN
+CtLsaPolicyAuditLogInfo()
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+
+ POLICY_AUDIT_LOG_INFO PolicyAuditLogInfo;
+
+ LARGE_INTEGER SystemTime;
+ LARGE_INTEGER TimeToShutdown;
+ LARGE_INTEGER AuditRetentionPeriod;
+
+ //
+ // Setup sample Audit Log Info
+ //
+
+ Status = NtQuerySystemTime(&SystemTime);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("NtQuerySystemTime failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ return(BooleanStatus);
+ }
+
+
+ TimeToShutdown.HighPart = 0xABCD;
+ TimeToShutdown.LowPart = 0x12345678L;
+
+ AuditRetentionPeriod.HighPart = TimeToShutdown.HighPart;
+ AuditRetentionPeriod.LowPart = TimeToShutdown.LowPart;
+
+ PolicyAuditLogInfo.AuditLogPercentFull = (ULONG) 50;
+ PolicyAuditLogInfo.MaximumLogSize = (ULONG) 0x00001000L;
+ PolicyAuditLogInfo.AuditRetentionPeriod = AuditRetentionPeriod;
+ PolicyAuditLogInfo.TimeToShutdown = TimeToShutdown;
+ PolicyAuditLogInfo.AuditLogFullShutdownInProgress = TRUE;
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyAuditLogInformation,
+ &PolicyAuditLogInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+
+BOOLEAN
+CtLsaPolicyAuditEventsInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo;
+ POLICY_AUDIT_EVENT_TYPE EventType;
+ POLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions[AuditEventMaxType+1];
+ //
+ // Setup sample Audit Events Info
+ //
+
+ PolicyAuditEventsInfo.AuditingMode = TRUE;
+ PolicyAuditEventsInfo.MaximumAuditEventCount = AuditEventMaxType;
+
+ for (EventType = AuditEventPrivilegedService;
+ EventType < AuditEventMaxType;
+ EventType++) {
+
+ EventAuditingOptions[EventType] =
+ (POLICY_AUDIT_EVENT_SUCCESS | POLICY_AUDIT_EVENT_FAILURE);
+ }
+
+ PolicyAuditEventsInfo.EventAuditingOptions = EventAuditingOptions;
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyAuditEventsInformation,
+ &PolicyAuditEventsInfo
+ );
+
+ return(BooleanStatus);
+
+}
+
+
+BOOLEAN
+CtLsaPolicyPrimaryDomainInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ POLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo;
+
+ //
+ // Setup sample Primary Domain Info
+ //
+
+ RtlInitUnicodeString( &PolicyPrimaryDomainInfo.Name, L"BedrockA");
+ PolicyPrimaryDomainInfo.Sid = BedrockADomainSid;
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyPrimaryDomainInformation,
+ &PolicyPrimaryDomainInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicyPdAccountInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ POLICY_PD_ACCOUNT_INFO PolicyPdAccountInfo;
+
+ //
+ // Setup sample PD Account Info
+ //
+
+ RtlInitUnicodeString( &PolicyPdAccountInfo.Name, L"BedrockC");
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyPdAccountInformation,
+ &PolicyPdAccountInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicyAccountDomainInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ POLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+
+ //
+ // Setup sample Account Domain Info
+ //
+
+ RtlInitUnicodeString( &PolicyAccountDomainInfo.DomainName, L"BedrockB");
+ PolicyAccountDomainInfo.DomainSid = BedrockBDomainSid;
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+
+BOOLEAN
+CtLsaPolicyLsaServerRoleInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ POLICY_LSA_SERVER_ROLE_INFO PolicyLsaServerRoleInfo;
+
+ //
+ // Setup sample Policy Lsa Server Role Info
+ //
+
+ PolicyLsaServerRoleInfo.LsaServerRole = PolicyServerRolePrimary;
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyLsaServerRoleInformation,
+ &PolicyLsaServerRoleInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicyReplicaSourceInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ POLICY_REPLICA_SOURCE_INFO PolicyReplicaSourceInfo;
+
+ //
+ // Setup sample Policy Replica Source Info
+ //
+
+ RtlInitUnicodeString( &PolicyReplicaSourceInfo.ReplicaSource,L"BedrockD");
+ RtlInitUnicodeString( &PolicyReplicaSourceInfo.ReplicaAccountName,L"Barney");
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyReplicaSourceInformation,
+ &PolicyReplicaSourceInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicyDefaultQuotaInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ POLICY_DEFAULT_QUOTA_INFO PolicyDefaultQuotaInfo;
+
+ //
+ // Setup sample default quota limit values
+ //
+
+ PolicyDefaultQuotaInfo.QuotaLimits.PagedPoolLimit = CT_PAGED_POOL;
+ PolicyDefaultQuotaInfo.QuotaLimits.NonPagedPoolLimit = CT_NON_PAGED_POOL;
+ PolicyDefaultQuotaInfo.QuotaLimits.MinimumWorkingSetSize = CT_MIN_WORKING_SET;
+ PolicyDefaultQuotaInfo.QuotaLimits.MaximumWorkingSetSize = CT_MAX_WORKING_SET;
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyDefaultQuotaInformation,
+ &PolicyDefaultQuotaInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicyModificationInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ POLICY_MODIFICATION_INFO PolicyModificationInfo;
+
+ //
+ // Setup sample Modification Info values
+ //
+
+ PolicyModificationInfo.ModifiedId.LowPart = (ULONG) 0x00000000L;
+ PolicyModificationInfo.ModifiedId.HighPart = 3L;
+ PolicyModificationInfo.DatabaseCreationTime.LowPart = (ULONG) 0x00000000L;
+ PolicyModificationInfo.DatabaseCreationTime.HighPart = 4L;
+
+ BooleanStatus = CtLsaPolicySetQuerySub(
+ PolicyModificationInformation,
+ &PolicyModificationInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicySetQuerySub(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PVOID PolicyInformation
+ )
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandleSet = NULL;
+ LSA_HANDLE PolicyHandleQuery = NULL;
+ LSA_HANDLE SavePolicyHandle = NULL;
+ PVOID ReturnedPolicyInformation;
+ PVOID SavedPolicyInformation;
+ ACCESS_MASK DesiredAccess;
+ BOOLEAN PolicyInfoSaved = FALSE;
+
+ ACCESS_MASK RequiredAccessSetPolicy[PolicyModificationInformation+1] =
+ {
+ 0,
+ POLICY_AUDIT_LOG_ADMIN,
+ POLICY_SET_AUDIT_REQUIREMENTS,
+ POLICY_TRUST_ADMIN,
+ 0,
+ POLICY_TRUST_ADMIN,
+ POLICY_SERVER_ADMIN,
+ POLICY_SERVER_ADMIN,
+ POLICY_SET_DEFAULT_QUOTA_LIMITS,
+ 0
+ };
+
+
+ ACCESS_MASK RequiredAccessQueryPolicy[PolicyModificationInformation+1] =
+ {
+ 0,
+ POLICY_VIEW_AUDIT_INFORMATION,
+ POLICY_VIEW_AUDIT_INFORMATION,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ POLICY_GET_PRIVATE_INFORMATION,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ 0
+ };
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ DbgPrint("...Testing information class %d\n", InformationClass);
+
+ //
+ // If this Information Policy Class can be set via non-trusted
+ // callers, we need to save it.
+ //
+
+ if (CtLsaPolicySetInfoAllowed(InformationClass) &&
+ CtLsaPolicyQueryInfoAllowed(InformationClass)) {
+
+ //
+ // Open handle for saving the existing Policy Information (if any) for
+ // this class.
+ //
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ GENERIC_ALL,
+ &SavePolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle failed 0x%lx\n",
+ Status
+ );
+ goto SetQueryInfoPolicyError;
+ }
+
+ //
+ // Now query the Policy Information set to be saved.
+ //
+
+ try {
+
+ Status = LsaQueryInformationPolicy(
+ SavePolicyHandle,
+ InformationClass,
+ &SavedPolicyInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaPolicySetQuerySub\n");
+ DbgPrint(" within LsaQueryInformationPolicy call 1\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ PolicyInfoSaved = TRUE;
+ }
+
+ //
+ // Open a handle to the Policy Object with the access required for
+ // setting the given Policy Information Class. If the Class
+ // is not settable via LsaSetInformationPolicy(), ie requires
+ // a trusted client, the call is expected to fail even if we open
+ // the Policy Object with GENERIC_ALL.
+ //
+
+ if (CtLsaPolicySetInfoAllowed(InformationClass)) {
+
+ DesiredAccess = RequiredAccessSetPolicy[InformationClass];
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ DesiredAccess,
+ &PolicyHandleSet
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle failed 0x%lx\n",
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Set the Policy Information for the specified class
+ //
+
+ try {
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandleSet,
+ InformationClass,
+ PolicyInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaPolicySetQuerySub\n");
+ DbgPrint(" within LsaSetInformationPolicy call 1\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ //
+ // If setting of the InformationClass via LsaSetInformationPolicy()
+ // is allowed, we expect STATUS_SUCCESS, otherwise we expect
+ // STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaPolicySetInfoAllowed(InformationClass)) {
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LsaSetInformationPolicy - Class %d failed 0x%lx\n",
+ InformationClass,
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be set via LsaSetInformationPolicy
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("LsaSetInformationPolicy - Attempt to set ");
+ DbgPrint("prohibited Information Class should have\n");
+ DbgPrint(
+ "failed 0x%lx\n (STATUS_INVALID_PARAMETER)",
+ STATUS_INVALID_PARAMETER
+ );
+ DbgPrint("instead returned 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ Status = LsaClose(PolicyHandleSet);
+ PolicyHandleSet = NULL;
+
+ //
+ // Now open a handle to the Policy Object with the access required for
+ // querying the given Policy Information Class. If the Class
+ // is not queryable via LsaQueryInformationPolicy(), ie requires
+ // a trusted client, the call is expected to fail even if we open
+ // the Policy Object with GENERIC_ALL.
+ //
+
+ if (CtLsaPolicyQueryInfoAllowed(InformationClass)) {
+
+ DesiredAccess = RequiredAccessQueryPolicy[InformationClass];
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ DesiredAccess,
+ &PolicyHandleQuery
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle failed 0x%lx\n",
+ Status
+ );
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ //
+ // Now query the Policy Information set
+ //
+
+ try {
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandleQuery,
+ InformationClass,
+ &ReturnedPolicyInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaPolicySetQuerySub\n");
+ DbgPrint(" within LsaQueryInformationPolicy call 1\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ //
+ // If querying of the InformationClass via LsaQueryInformationPolicy()
+ // is allowed, we expect STATUS_SUCCESS, otherwise we expect
+ // STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaPolicyQueryInfoAllowed(InformationClass)) {
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LsaQueryInformationPolicy - Class %d failed 0x%lx\n",
+ InformationClass,
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be queried via LsaQueryInformationPolicy
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("LsaQueryInformationPolicy - Attempt to query ");
+ DbgPrint("prohibited Information Class should have\n");
+ DbgPrint(
+ "failed 0x%lx\n (STATUS_INVALID_PARAMETER)",
+ STATUS_INVALID_PARAMETER
+ );
+ DbgPrint("instead returned 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If set and query are both allowed for the Information Class, they
+ // should both have worked. In this case, compare the returned Policy
+ // information with that set
+ //
+
+ if (CtLsaPolicySetInfoAllowed(InformationClass) &&
+ CtLsaPolicyQueryInfoAllowed(InformationClass)) {
+
+ BooleanStatus &= CtLsaPolicyInfoClassCompare(
+ InformationClass,
+ PolicyInformation,
+ ReturnedPolicyInformation
+ );
+ }
+
+ Status = LsaClose(PolicyHandleQuery);
+ PolicyHandleQuery = NULL;
+
+ if (ReturnedPolicyInformation != NULL) {
+
+ Status = LsaFreeMemory(ReturnedPolicyInformation);
+ ReturnedPolicyInformation = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Query Info Policy - LsaFreeMemory(ReturnedPolicyInformation)");
+ DbgPrint(" failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // Now open a handle to the Policy Object with all accesses except the
+ // access required for setting the given Policy Information Class.
+ // If the Policy Information Class cannot be set via public API,
+ // open the handle instead with POLICY_ALL_ACCESS.
+ //
+
+ if (CtLsaPolicySetInfoAllowed(InformationClass)) {
+
+ DesiredAccess =
+ (POLICY_ALL_ACCESS & ~RequiredAccessSetPolicy[InformationClass]);
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ DesiredAccess,
+ &PolicyHandleSet
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle failed 0x%lx\n",
+ Status
+ );
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ //
+ // Attempt to set the Policy Information for the specified class
+ // using the handle opened above. This handle has either the
+ // complement of the accesses required (if the class is settable
+ // via LsaSetInformationPolicy()) or POLICY_ALL_ACCESS. In the
+ // former case, we should get STATUS_ACCESS_DENIED back from
+ // LsaSetInformationPolicy(), in the latter case, STATUS_INVALID_PARAMETER.
+ //
+
+ try {
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandleSet,
+ InformationClass,
+ PolicyInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaPolicySetQuerySub\n");
+ DbgPrint(" within LsaSetInformationPolicy call 2\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // If setting of the InformationClass via LsaSetInformationPolicy()
+ // is allowed, we expect STATUS_ACCESS_DENIED since we specified the
+ // complement of the access required (relative to POLICY_ALL_ACCESS).
+ // Otherwise we expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaPolicySetInfoAllowed(InformationClass)) {
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LsaSetInformationPolicy Class");
+
+ DbgPrint(
+ " %d access mask 0x%lx should have failed 0x%lx (STATUS_ACCESS_DENIED)\n",
+ InformationClass,
+ DesiredAccess,
+ STATUS_ACCESS_DENIED
+ );
+
+ DbgPrint(
+ "since the required access for setting info is 0x%lx\n",
+ RequiredAccessSetPolicy[InformationClass]
+ );
+
+ DbgPrint(" but instead returned 0x%lx\n", Status);
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be set via LsaSetInformationPolicy
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint(
+ "LsaSetInformationPolicy - Attempt to set\n"
+ "prohibited Information Class should have\n"
+ "failed 0x%lx (STATUS_INVALID_PARAMETER)\n"
+ "instead returned 0x%lx\n",
+ STATUS_INVALID_PARAMETER,
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ Status = LsaClose(PolicyHandleSet);
+ PolicyHandleSet = NULL;
+
+ //
+ // Now open a handle to the Policy Object with all accesses except the
+ // access required for querying the given Policy Information Class.
+ // If the Policy Information Class cannot be queried via public API,
+ // open the handle instead with POLICY_ALL_ACCESS.
+ //
+
+ if (CtLsaPolicyQueryInfoAllowed(InformationClass)) {
+
+ DesiredAccess =
+ (POLICY_ALL_ACCESS & ~RequiredAccessQueryPolicy[InformationClass]);
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ DesiredAccess,
+ &PolicyHandleQuery
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle failed 0x%lx\n",
+ Status
+ );
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ try {
+
+ //
+ // Now query the Policy Information set. This should fail
+ // STATUS_ACCESS_DENIED
+ //
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandleQuery,
+ InformationClass,
+ &ReturnedPolicyInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaPolicySetQuerySub\n");
+ DbgPrint(" within LsaQueryInformationPolicy call 2\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // If querying of the InformationClass via LsaQueryInformationPolicy()
+ // is allowed, we expect STATUS_ACCESS_DENIED since we specified the
+ // complement of the access required (relative to POLICY_ALL_ACCESS).
+ // Otherwise we expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaPolicyQueryInfoAllowed(InformationClass)) {
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LsaQueryInformationPolicy Class");
+
+ DbgPrint(
+ " %d access mask 0x%lx should have failed 0x%lx (STATUS_ACCESS_DENIED)\n",
+ InformationClass,
+ DesiredAccess,
+ STATUS_ACCESS_DENIED
+ );
+
+ DbgPrint(
+ "since the required access for querying info is 0x%lx\n",
+ RequiredAccessQueryPolicy[InformationClass]
+ );
+
+ DbgPrint(" but instead returned 0x%lx\n", Status);
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be queried via LsaQueryInformationPolicy
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("LsaQueryInformationPolicy - Attempt to query ");
+ DbgPrint("prohibited Information Class should have\n");
+ DbgPrint(
+ "failed 0x%lx\n (STATUS_INVALID_PARAMETER)",
+ STATUS_INVALID_PARAMETER
+ );
+ DbgPrint("instead returned 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ if (ReturnedPolicyInformation != NULL) {
+
+ Status = LsaFreeMemory(ReturnedPolicyInformation);
+ ReturnedPolicyInformation = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Query Info Policy - LsaFreeMemory(ReturnedPolicyInformation)");
+ DbgPrint(" failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+SetQueryInfoPolicyFinish:
+
+ //
+ // If we saved the old Policy Info, we need to restore it.
+ //
+
+ if (PolicyInfoSaved) {
+
+ PolicyInfoSaved = FALSE;
+
+ try {
+
+ Status = LsaSetInformationPolicy(
+ SavePolicyHandle,
+ InformationClass,
+ SavedPolicyInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaPolicySetQuerySub\n");
+ DbgPrint(" within LsaQueryInformationPolicy call 1\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto SetQueryInfoPolicyError;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Info Policy Test failed\n"
+ "... failed to restore info policy for class %d\n"
+ "LsaSetInformationPolicy returned 0x%lx\n",
+ InformationClass,
+ Status
+ );
+ goto SetQueryInfoPolicyError;
+ }
+ }
+
+ if (PolicyHandleQuery != NULL) {
+
+ Status = LsaClose(PolicyHandleQuery);
+
+ PolicyHandleQuery = NULL;
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto SetQueryInfoPolicyError;
+ }
+ }
+
+ return(BooleanStatus);
+
+SetQueryInfoPolicyError:
+
+ BooleanStatus = FALSE;
+
+ goto SetQueryInfoPolicyFinish;
+}
+
+
+BOOLEAN
+CtLsaPolicyInfoClassCompare(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PVOID PolicyInformation1,
+ IN PVOID PolicyInformation2
+ )
+
+/*++
+
+Routine Description:
+
+ This function compares two sets of Policy Information for a known
+ Information Class.
+
+Arguments:
+
+ InformationClass - Specifies a Policy Information Class
+
+ PolicyInformation1 - First comparand. Policy Information for the
+ given information class.
+
+
+ PolicyInformation2 - Second comparand. Policy Information for the
+ given information class.
+
+Return Values:
+
+ BOOLEAN - TRUE if policy information sets are equal , else FALSE
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ PPOLICY_AUDIT_LOG_INFO PolicyAuditLogInfo1;
+ PPOLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo1;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo1;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo1;
+ PPOLICY_PD_ACCOUNT_INFO PolicyPdAccountInfo1;
+ PPOLICY_LSA_SERVER_ROLE_INFO PolicyLsaServerRoleInfo1;
+ PPOLICY_REPLICA_SOURCE_INFO PolicyReplicaSourceInfo1;
+ PPOLICY_DEFAULT_QUOTA_INFO PolicyDefaultQuotaInfo1;
+ PPOLICY_MODIFICATION_INFO PolicyModificationInfo1;
+
+ PPOLICY_AUDIT_LOG_INFO PolicyAuditLogInfo2;
+ PPOLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo2;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo2;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo2;
+ PPOLICY_PD_ACCOUNT_INFO PolicyPdAccountInfo2;
+ PPOLICY_LSA_SERVER_ROLE_INFO PolicyLsaServerRoleInfo2;
+ PPOLICY_REPLICA_SOURCE_INFO PolicyReplicaSourceInfo2;
+ PPOLICY_DEFAULT_QUOTA_INFO PolicyDefaultQuotaInfo2;
+ PPOLICY_MODIFICATION_INFO PolicyModificationInfo2;
+
+ PQUOTA_LIMITS QuotaLimits1, QuotaLimits2;
+
+ //
+ // Switch on Information Class
+ //
+
+ switch (InformationClass) {
+
+ case PolicyAuditLogInformation:
+
+ PolicyAuditLogInfo1 = (PPOLICY_AUDIT_LOG_INFO) PolicyInformation1;
+ PolicyAuditLogInfo2 = (PPOLICY_AUDIT_LOG_INFO) PolicyInformation2;
+
+ if (PolicyAuditLogInfo1->AuditLogPercentFull !=
+ PolicyAuditLogInfo2->AuditLogPercentFull) {
+
+ DbgPrint("AuditLogPercentFull returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (PolicyAuditLogInfo1->MaximumLogSize !=
+ PolicyAuditLogInfo2->MaximumLogSize) {
+
+ DbgPrint("MaximumLogSize returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!(PolicyAuditLogInfo1->AuditRetentionPeriod.QuadPart ==
+ PolicyAuditLogInfo2->AuditRetentionPeriod.QuadPart)) {
+
+ DbgPrint("AuditRetentionPeriod returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (PolicyAuditLogInfo1->AuditLogFullShutdownInProgress !=
+ PolicyAuditLogInfo2->AuditLogFullShutdownInProgress) {
+
+ DbgPrint("AuditLogFullShutdownInProgress returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!(PolicyAuditLogInfo1->TimeToShutdown.QuadPart ==
+ PolicyAuditLogInfo2->TimeToShutdown.QuadPart)) {
+
+ DbgPrint("TimeToShutDown returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case PolicyAuditEventsInformation:
+
+ PolicyAuditEventsInfo1 = (PPOLICY_AUDIT_EVENTS_INFO) PolicyInformation1;
+ PolicyAuditEventsInfo2 = (PPOLICY_AUDIT_EVENTS_INFO) PolicyInformation2;
+
+ if (PolicyAuditEventsInfo1->AuditingMode !=
+ PolicyAuditEventsInfo2->AuditingMode) {
+
+ DbgPrint("Auditing Mode returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (PolicyAuditEventsInfo1->MaximumAuditEventCount !=
+ PolicyAuditEventsInfo2->MaximumAuditEventCount) {
+
+ DbgPrint("MaximumAuditEventCount returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (memcmp(
+ PolicyAuditEventsInfo1->EventAuditingOptions,
+ PolicyAuditEventsInfo2->EventAuditingOptions,
+ PolicyAuditEventsInfo1->MaximumAuditEventCount *
+ (ULONG) sizeof (POLICY_AUDIT_EVENT_OPTIONS)
+ ) != 0) {
+
+ DbgPrint("EventAuditingOptions returned are incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case PolicyPrimaryDomainInformation:
+
+ PolicyPrimaryDomainInfo1 = (PPOLICY_PRIMARY_DOMAIN_INFO) PolicyInformation1;
+ PolicyPrimaryDomainInfo2 = (PPOLICY_PRIMARY_DOMAIN_INFO) PolicyInformation2;
+
+ if (!RtlEqualUnicodeString(
+ &PolicyPrimaryDomainInfo1->Name,
+ &PolicyPrimaryDomainInfo2->Name,
+ FALSE
+ )) {
+
+ DbgPrint("Policy Info Class %d - mismatch on Name\n", InformationClass);
+ BooleanStatus = FALSE;
+ }
+
+ if (!RtlEqualSid(
+ PolicyPrimaryDomainInfo1->Sid,
+ PolicyPrimaryDomainInfo2->Sid
+ )) {
+
+ DbgPrint("Policy Info Class %d - mismatch on Sid\n", InformationClass);
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case PolicyAccountDomainInformation:
+
+ PolicyAccountDomainInfo1 = (PPOLICY_ACCOUNT_DOMAIN_INFO) PolicyInformation1;
+ PolicyAccountDomainInfo2 = (PPOLICY_ACCOUNT_DOMAIN_INFO) PolicyInformation2;
+
+
+ if (!RtlEqualUnicodeString(
+ &PolicyAccountDomainInfo1->DomainName,
+ &PolicyAccountDomainInfo2->DomainName,
+ FALSE
+ )) {
+
+ DbgPrint("Policy Info Class %d - mismatch on DomainName\n", InformationClass);
+ BooleanStatus = FALSE;
+ }
+
+ if (!RtlEqualSid(
+ PolicyAccountDomainInfo1->DomainSid,
+ PolicyAccountDomainInfo2->DomainSid
+ )) {
+
+ DbgPrint("Policy Info Class %d - mismatch on DomainSid\n", InformationClass);
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case PolicyPdAccountInformation:
+
+ PolicyPdAccountInfo1 = (PPOLICY_PD_ACCOUNT_INFO) PolicyInformation1;
+ PolicyPdAccountInfo2 = (PPOLICY_PD_ACCOUNT_INFO) PolicyInformation2;
+
+ if (!RtlEqualUnicodeString(
+ &PolicyPdAccountInfo1->Name,
+ &PolicyPdAccountInfo2->Name,
+ FALSE
+ )) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on Name\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case PolicyLsaServerRoleInformation:
+
+ PolicyLsaServerRoleInfo1 = (PPOLICY_LSA_SERVER_ROLE_INFO) PolicyInformation1;
+ PolicyLsaServerRoleInfo2 = (PPOLICY_LSA_SERVER_ROLE_INFO) PolicyInformation2;
+
+ if (PolicyLsaServerRoleInfo1->LsaServerRole !=
+ PolicyLsaServerRoleInfo2->LsaServerRole) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on LsaServerRole\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+
+ }
+
+ break;
+
+ case PolicyReplicaSourceInformation:
+
+ PolicyReplicaSourceInfo1 = (PPOLICY_REPLICA_SOURCE_INFO) PolicyInformation1;
+ PolicyReplicaSourceInfo2 = (PPOLICY_REPLICA_SOURCE_INFO) PolicyInformation2;
+
+ if (!RtlEqualUnicodeString(
+ &PolicyReplicaSourceInfo1->ReplicaSource,
+ &PolicyReplicaSourceInfo2->ReplicaSource,
+ FALSE
+ )) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on Replica Source\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!RtlEqualUnicodeString(
+ &PolicyReplicaSourceInfo1->ReplicaAccountName,
+ &PolicyReplicaSourceInfo2->ReplicaAccountName,
+ FALSE
+ )) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on ReplicaAccountName\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case PolicyDefaultQuotaInformation:
+
+ PolicyDefaultQuotaInfo1 = (PPOLICY_DEFAULT_QUOTA_INFO) PolicyInformation1;
+ PolicyDefaultQuotaInfo2 = (PPOLICY_DEFAULT_QUOTA_INFO) PolicyInformation2;
+
+ QuotaLimits1 = &PolicyDefaultQuotaInfo1->QuotaLimits;
+ QuotaLimits2 = &PolicyDefaultQuotaInfo2->QuotaLimits;
+
+ if (QuotaLimits1->PagedPoolLimit !=
+ QuotaLimits2->PagedPoolLimit) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on QuotaLimits PagedPoolLimit\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (QuotaLimits1->NonPagedPoolLimit !=
+ QuotaLimits2->NonPagedPoolLimit) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on QuotaLimits NonPagedPoolLimit\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (QuotaLimits1->MinimumWorkingSetSize !=
+ QuotaLimits2->MinimumWorkingSetSize) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on QuotaLimits MinimumWorkingSetSize\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (QuotaLimits1->MaximumWorkingSetSize !=
+ QuotaLimits2->MaximumWorkingSetSize) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on QuotaLimits MaximumWorkingSetSize\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (QuotaLimits1->PagefileLimit !=
+ QuotaLimits2->PagefileLimit) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on QuotaLimits PagefileLimit\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!(QuotaLimits1->TimeLimit.QuadPart ==
+ QuotaLimits2->TimeLimit.QuadPart
+ )) {
+
+ DbgPrint(
+ "Policy Info Class %d - mismatch on QuotaLimits TimeLimit\n",
+ InformationClass
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+
+ break;
+
+ case PolicyModificationInformation:
+
+ PolicyModificationInfo1 = (PPOLICY_MODIFICATION_INFO) PolicyInformation1;
+ PolicyModificationInfo2 = (PPOLICY_MODIFICATION_INFO) PolicyInformation2;
+
+ if (!(PolicyModificationInfo1->ModifiedId.QuadPart ==
+ PolicyModificationInfo2->ModifiedId.QuadPart )) {
+
+ DbgPrint("ModifiedId returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!(PolicyModificationInfo1->DatabaseCreationTime.QuadPart ==
+ PolicyModificationInfo2->DatabaseCreationTime.QuadPart )) {
+
+ DbgPrint("DatabaseCreationTime returned is incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ default:
+
+ BooleanStatus = FALSE;
+
+ DbgPrint(
+ "Bug in test program - Invalid Information Class %d\n",
+ InformationClass
+ );
+
+ break;
+ }
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaPolicyQueryInfoAllowed(
+ IN POLICY_INFORMATION_CLASS InformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether querying of a Policy Information Class
+ is allowed via LsaQueryInformationPolicy. Note that all Policy
+ Information Classes may be queried via (to be implemented) trusted
+ clients to private API.
+
+Arguments:
+
+ InformationClass - The Policy Information Class to be checked (assumed
+ valid).
+
+Return Value:
+
+ BOOLEAN - TRUE if the information class can be queried via
+ LsaQueryInformationPolicy() else FALSE.
+
+--*/
+
+{
+ //
+ // Range check the InformationClass parameter.
+ //
+
+ if ((InformationClass < PolicyAuditLogInformation) ||
+ (InformationClass > PolicyModificationInformation)) {
+
+ DbgPrint("WARNING! CtLsaPolicyQueryInfoAllowed:\n");
+
+ DbgPrint(
+ " InformationClass %d is invalid - check caller\n",
+ InformationClass
+ );
+
+ return(FALSE);
+ }
+
+ //
+ // Check if this Information Class can be queried via
+ // LsaQueryInformationPolicy()
+ //
+
+ if (InformationClass == PolicyModificationInformation) {
+
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+BOOLEAN
+CtLsaPolicySetInfoAllowed(
+ IN POLICY_INFORMATION_CLASS InformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether setting of a Policy Information Class
+ is allowed via LsaSetInformationPolicy. Note that all Policy
+ Information Classes may be set via (to be implemented) trusted
+ clients to private API.
+
+Arguments:
+
+ InformationClass - The Policy Information Class to be checked (assumed
+ valid).
+
+Return Value:
+
+ BOOLEAN - TRUE idf the information class can be set via
+ LsaSetInformationPolicy() else FALSE.
+
+--*/
+
+{
+ //
+ // Range check the InformationClass parameter.
+ //
+
+ if ((InformationClass < PolicyAuditLogInformation) ||
+ (InformationClass > PolicyModificationInformation)) {
+
+ DbgPrint("WARNING! CtLsaPolicySetInfoAllowed:\n");
+
+ DbgPrint(
+ " InformationClass %d is invalid - check caller\n",
+ InformationClass
+ );
+
+ return(FALSE);
+ }
+
+ //
+ // Check if this Information Class can be set via
+ // LsaSetInformationPolicy().
+ //
+
+ if (InformationClass == PolicyPdAccountInformation) {
+
+ return(FALSE);
+ }
+
+ if (InformationClass == PolicyModificationInformation) {
+
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+BOOLEAN
+CtLsaAccountObject(
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the Lsa Account Object API.
+
+Arguments:
+
+ TrustedClient - Specifies whether Trusted Client variations
+ are to be run additionally. NOTE: These can only be run
+ with ctlsarpc -lsainit... substituted for lsass.exe
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("********************************************************\n");
+ DbgPrint("LSA RPC CT - Test Lsa Account Object API\n");
+ DbgPrint("********************************************************\n");
+
+ //
+ // Open a handle to the LSA
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ GENERIC_ALL,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Lsa Open failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Test account creation. Abandon account testing if error.
+ //
+
+ if (!CtLsaAccountCreate()) {
+
+ return FALSE;
+ }
+
+ //
+ // Test account open and close. Abandon account testing if error.
+ //
+
+ if (!CtLsaAccountOpenClose()) {
+
+ return FALSE;
+ }
+
+ //
+ // Test account privileges.
+ //
+
+ if (!CtLsaAccountPrivileges()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Test account quota limits.
+ //
+
+ if (!CtLsaAccountQuotaLimits()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Test account enumeration.
+ //
+
+ if (!CtLsaAccountEnumeration()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Test account System Access.
+ //
+
+ if (!CtLsaAccountSystemAccess()) {
+
+ BooleanStatus = FALSE;
+ }
+ //
+
+ // Test account deletion.
+ //
+
+ if (!CtLsaAccountDelete()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ return BooleanStatus;
+
+ DBG_UNREFERENCED_PARAMETER( TrustedClient );
+}
+
+
+BOOLEAN
+CtLsaAccountCreate(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LSA API that Create accounts.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ DbgPrint("[1] - Test Account Creation API\n");
+
+ //
+ // Create the new accounts in the LSA Database.
+ //
+
+ DesiredAccessFred = GENERIC_ALL;
+
+ Status = LsaCreateAccount(
+ PolicyHandle,
+ FredSid,
+ DesiredAccessFred,
+ &AccountHandleFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Fred Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessWilma = GENERIC_READ;
+
+ Status = LsaCreateAccount(
+ PolicyHandle,
+ WilmaSid,
+ DesiredAccessWilma,
+ &AccountHandleWilma
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Wilma Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessPebbles = GENERIC_WRITE;
+
+ Status = LsaCreateAccount(
+ PolicyHandle,
+ PebblesSid,
+ DesiredAccessPebbles,
+ &AccountHandlePebbles
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Pebbles Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessDino = GENERIC_EXECUTE;
+
+ Status = LsaCreateAccount(
+ PolicyHandle,
+ DinoSid,
+ DesiredAccessDino,
+ &AccountHandleDino
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Dino Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Close the accounts just created
+ //
+
+ Status = LsaClose(AccountHandleFred);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new Fred account failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ Status = LsaClose(AccountHandleWilma);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new Wilma account failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ Status = LsaClose(AccountHandlePebbles);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Close new Pebbles account failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaClose(AccountHandleDino);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new Dino account failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+CtLsaAccountOpenClose(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LSA API that open and close accounts.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus;
+ PLSA_HANDLE BadHandleAddress;
+
+ DbgPrint("[2] - Test Account Open and Close API\n");
+
+ //
+ // Open the accounts created by CtLsaAccountCreate
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ FredSid,
+ DesiredAccessFred,
+ &AccountHandleFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Fred Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ WilmaSid,
+ DesiredAccessWilma,
+ &AccountHandleWilma
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Wilma Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ PebblesSid,
+ DesiredAccessPebbles,
+ &AccountHandlePebbles
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Pebbles Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Open Dino account.
+ // This is a spare call and should be changed in some way.
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ DinoSid,
+ DesiredAccessDino,
+ &AccountHandleDino
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Dino Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Now open each account a second time so we have 2 handles to each
+ // account.
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ FredSid,
+ DesiredAccessFred,
+ &AccountHandleFred2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open Fred Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ WilmaSid,
+ DesiredAccessWilma,
+ &AccountHandleWilma2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open Wilma Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ PebblesSid,
+ DesiredAccessPebbles,
+ &AccountHandlePebbles2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open Pebbles Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ DinoSid,
+ DesiredAccessDino,
+ &AccountHandleDino2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Open Dino Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Close the 2nd handles to the accounts
+ //
+
+ Status = LsaClose( AccountHandleFred2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Fred Acct hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( AccountHandleWilma2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Wilma Acct hdl2 failed 0x%lx\n",Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( AccountHandlePebbles2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Pebbles Acct hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( AccountHandleDino2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Dino Acct hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ BooleanStatus = TRUE;
+
+ //
+ // Try to create Fred again. We should get STATUS_OBJECT_NAME_COLLISION
+ //
+
+ Status = LsaCreateAccount(
+ PolicyHandle,
+ FredSid,
+ GENERIC_ALL,
+ &AccountHandleFred3
+ );
+
+ if (Status != STATUS_OBJECT_NAME_COLLISION) {
+
+ DbgPrint("LSA RPC CT - Create Fred Acct when Fred exists\n");
+ DbgPrint("and LSA_OBJECT_CREATE disposition specified\n");
+ DbgPrint("Expected 0x%lx (STATUS_OBJECT_NAME_COLLISION)\n",
+ STATUS_OBJECT_NAME_COLLISION);
+ DbgPrint("Got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ return BooleanStatus;
+
+ if (Level == 2) {
+
+ //
+ // LsaOpenAccount with bad addresses
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ FredSid,
+ DesiredAccessFred,
+ BadHandleAddress
+ );
+
+ //
+ // Lsa Close with bad address
+ //
+
+ Status = LsaClose( (LSA_HANDLE) BadAddress );
+ }
+}
+
+
+BOOLEAN
+CtLsaAccountPrivileges(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests thei API that manipulate LSA Account Privileges
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PPRIVILEGE_SET *BadPrivilegeSetAddress = NULL;
+
+ DbgPrint("[3] - Test Account Privileges API\n");
+
+ //
+ // Add three privileges to the Fred account - BACKUP, RESTORE and
+ // LOCK_MEMORY
+ //
+
+ PrivilegesAddFred = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ sizeof (PRIVILEGE_SET) +
+ 2 * sizeof (LUID_AND_ATTRIBUTES)
+ );
+
+ if (PrivilegesAddFred == NULL) {
+
+ DbgPrint("LSA RPC CT - Alloc Mem for PrivilegesAddFred failed\n");
+ return FALSE;
+ }
+
+ PrivilegesAddFred->PrivilegeCount = 3;
+ PrivilegesAddFred->Control = 0;
+ PrivilegesAddFred->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
+ PrivilegesAddFred->Privilege[0].Luid.HighPart = 0;
+ PrivilegesAddFred->Privilege[0].Attributes =
+ SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+
+ PrivilegesAddFred->Privilege[1].Luid.LowPart = SE_RESTORE_PRIVILEGE;
+ PrivilegesAddFred->Privilege[1].Luid.HighPart = 0;
+ PrivilegesAddFred->Privilege[1].Attributes =
+ SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+
+ PrivilegesAddFred->Privilege[2].Luid.LowPart = SE_LOCK_MEMORY_PRIVILEGE;
+ PrivilegesAddFred->Privilege[2].Luid.HighPart = 0;
+ PrivilegesAddFred->Privilege[2].Attributes =
+ SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+
+ Status = LsaAddPrivilegesToAccount(
+ AccountHandleFred,
+ PrivilegesAddFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("LSA RPC CT - Add privs Fred Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // 1st enumeration of privileges of Fred. There should be three.
+ //
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandleFred,
+ &PrivilegesEnumFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("LSA RPC CT - Enum privs Fred Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Supersede the first two privileges and add another privilege to
+ // the Fred account - supersede BACKUP, RESTORE and add
+ // INCREASE_QUOTA
+ //
+
+ PrivilegesAddFred = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ sizeof (PRIVILEGE_SET) +
+ 2 * sizeof (LUID_AND_ATTRIBUTES)
+ );
+
+ if (PrivilegesAddFred == NULL) {
+
+ DbgPrint("LSA RPC CT - Alloc Mem for PrivilegesAddFred failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ PrivilegesAddFred->PrivilegeCount = 3;
+ PrivilegesAddFred->Control = 0;
+ PrivilegesAddFred->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
+ PrivilegesAddFred->Privilege[0].Luid.HighPart = 0;
+ PrivilegesAddFred->Privilege[0].Attributes =
+ SE_PRIVILEGE_ENABLED;
+
+ PrivilegesAddFred->Privilege[1].Luid.LowPart = SE_RESTORE_PRIVILEGE;
+ PrivilegesAddFred->Privilege[1].Luid.HighPart = 0;
+ PrivilegesAddFred->Privilege[1].Attributes =
+ SE_PRIVILEGE_ENABLED;
+
+ PrivilegesAddFred->Privilege[2].Luid.LowPart =
+ SE_INCREASE_QUOTA_PRIVILEGE;
+ PrivilegesAddFred->Privilege[2].Luid.HighPart = 0;
+ PrivilegesAddFred->Privilege[2].Attributes =
+ SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+
+ if (PrivilegesAddFred == NULL) {
+
+ DbgPrint("LSA RPC CT - Alloc Mem for PrivilegesAddFred failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ Status = LsaAddPrivilegesToAccount(
+ AccountHandleFred,
+ PrivilegesAddFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Add privs Fred Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // 2nd enumeration of privileges of Fred. There should be four.
+ // BACKUP, RESTORE, LOCK_MEMORY and INCREASE_QUOTA
+ //
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandleFred,
+ &PrivilegesEnumFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - 2nd Enum privs Fred Acct failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ //
+ // Remove the SE_LOCK_MEMORY_PRIVILEGE
+ //
+
+ PrivilegesRemoveFred = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ sizeof (PRIVILEGE_SET) +
+ 2 * sizeof (LUID_AND_ATTRIBUTES)
+ );
+
+ if (PrivilegesRemoveFred == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Alloc Mem for PrivilegesAddFred failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ PrivilegesRemoveFred->PrivilegeCount = 1;
+ PrivilegesRemoveFred->Control = 0;
+ PrivilegesRemoveFred->Privilege[0].Luid.LowPart = SE_LOCK_MEMORY_PRIVILEGE;
+ PrivilegesRemoveFred->Privilege[0].Luid.HighPart = 0;
+ PrivilegesRemoveFred->Privilege[0].Attributes = 0;
+
+ Status = LsaRemovePrivilegesFromAccount(
+ AccountHandleFred,
+ FALSE,
+ PrivilegesRemoveFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Remove some privs Fred Acct failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ //
+ // 3rd enumeration of privileges of Fred. There should be three.
+ // BACKUP, RESTORE, INCREASE_QUOTA
+ //
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandleFred,
+ &PrivilegesEnumFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - 3rd Enum privs Fred Acct failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+
+ Status = LsaRemovePrivilegesFromAccount(
+ AccountHandleFred,
+ TRUE,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Remove all privs Fred Acct failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ //
+ // 4th enumeration of privileges of Fred. There should be none.
+ //
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandleFred,
+ &PrivilegesEnumFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - 4th Enum privs Fred Acct failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ if (Level == 2) {
+
+ Status = LsaAddPrivilegesToAccount(
+ (LSA_HANDLE) BadAddress,
+ PrivilegesAddFred
+ );
+
+ Status = LsaAddPrivilegesToAccount(
+ AccountHandleFred,
+ (PPRIVILEGE_SET) BadAddress
+ );
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ (LSA_HANDLE) BadAddress,
+ &PrivilegesEnumFred
+ );
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandleFred,
+ BadPrivilegeSetAddress
+ );
+
+ Status = LsaRemovePrivilegesFromAccount(
+ (LSA_HANDLE) BadAddress,
+ FALSE,
+ PrivilegesRemoveFred
+ );
+
+ Status = LsaRemovePrivilegesFromAccount(
+ AccountHandleFred,
+ FALSE,
+ (PPRIVILEGE_SET) BadAddress
+ );
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+CtLsaAccountQuotaLimits(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LSA API that manipulate Account Quota Limits.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("[4] - Test Account Quota Limits API\n");
+
+ Status = LsaGetQuotasForAccount(
+ AccountHandleFred,
+ &QuotaLimitsGetFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Get quotas Fred Acct failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ QuotaLimitsSetFred.PagedPoolLimit = 0x02000000;
+ QuotaLimitsSetFred.NonPagedPoolLimit = 0x01000000;
+ QuotaLimitsSetFred.MinimumWorkingSetSize = 0x03000000;
+ QuotaLimitsSetFred.MaximumWorkingSetSize = 0x04000000;
+ QuotaLimitsSetFred.PagefileLimit = 0x05000000;
+ QuotaLimitsSetFred.TimeLimit.LowPart = 0x06000000;
+ QuotaLimitsSetFred.TimeLimit.HighPart = 0;
+
+ Status = LsaSetQuotasForAccount(
+ AccountHandleFred,
+ &QuotaLimitsSetFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Set quotas Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaGetQuotasForAccount(
+ AccountHandleFred,
+ &QuotaLimitsGetFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Get quotas Fred Acct failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ if (!CtEqualQuotaLimits(&QuotaLimitsGetFred, &QuotaLimitsSetFred)) {
+
+ DbgPrint("One or more quota limits did not match\n");
+ return FALSE;
+ }
+
+ //
+ // Bad Addresses passed to Lsa Quota Limit API
+ //
+
+ if (Level == 2) {
+
+ Status = LsaGetQuotasForAccount(
+ (LSA_HANDLE) BadAddress,
+ &QuotaLimitsGetFred
+ );
+
+ Status = LsaGetQuotasForAccount(
+ AccountHandleFred,
+ (PQUOTA_LIMITS) BadAddress
+ );
+
+ Status = LsaSetQuotasForAccount(
+ (LSA_HANDLE) BadAddress,
+ &QuotaLimitsSetFred
+ );
+
+ Status = LsaSetQuotasForAccount(
+ AccountHandleFred,
+ (PQUOTA_LIMITS) BadAddress
+ );
+
+ //
+ // Try using the 2nd handle of the Fred account after closure
+ //
+
+ Status = LsaGetQuotasForAccount(
+ AccountHandleFred2,
+ &QuotaLimitsGetFred
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ DbgPrint("Using account handle after closure did not fail\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Try using the wrong handle
+ //
+
+ Status = LsaGetQuotasForAccount( PolicyHandle, &QuotaLimitsGetFred );
+
+ if (Status != STATUS_INVALID_HANDLE) {
+
+ DbgPrint("LSA RPC CT - Using incorrect handle status incorrect\n");
+ DbgPrint(
+ "Expected 0x%lx (STATUS_INVALID_HANDLE))\n",
+ STATUS_INVALID_HANDLE
+ );
+ DbgPrint("Got 0x%lx\n", Status);
+ }
+ }
+
+ return BooleanStatus;
+}
+
+
+BOOLEAN
+CtLsaAccountEnumeration(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the enumeration of accounts in the LSA
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG Base = 0;
+ ULONG CountReturned = 0;
+ ULONG PreferedMaximumLength;
+ ULONG EnumerationContext = 0;
+ ULONG EnumNumber = 0;
+ PLSA_ENUMERATION_INFORMATION EnumerationInformation;
+ PVOID EnumerationInformationVoid = NULL;
+ PVOID *BadEnumerationAddress = NULL;
+ PULONG BadCountReturnedAddress = NULL;
+
+ DbgPrint("[5] - Test Account Enumeration API\n");
+
+ //
+ // Enumerate the accounts in the Lsa
+ //
+
+ PreferedMaximumLength = RtlLengthSid(FredSid) +
+ sizeof(LSA_ENUMERATION_INFORMATION) +
+ RtlLengthSid(WilmaSid) +
+ sizeof(LSA_ENUMERATION_INFORMATION);
+
+ Status = LsaEnumerateAccounts(
+ PolicyHandle,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ EnumerationInformation =
+ (PLSA_ENUMERATION_INFORMATION) EnumerationInformationVoid;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Enumeration of first two accounts failed\n");
+ DbgPrint("0x%lx\n", Status);
+ goto EnumerateAccountsError;
+ }
+
+ if (CountReturned < 1 || CountReturned > 3) {
+
+ DbgPrint("LSA_RPC_CT - CountReturned invalid\n");
+ DbgPrint("expected 1 <= CountReturned <= 3\n");
+ DbgPrint("got %d\n", CountReturned);
+ BooleanStatus = FALSE;
+ }
+
+ AccountSidInfo[0].Sid = FredSid;
+ AccountSidInfo[1].Sid = WilmaSid;
+ AccountSidInfo[3].Sid = PebblesSid;
+ AccountSidInfo[2].Sid = DinoSid;
+
+ AccountSidInfo[0].SidFound = FALSE;
+ AccountSidInfo[1].SidFound = FALSE;
+ AccountSidInfo[2].SidFound = FALSE;
+ AccountSidInfo[3].SidFound = FALSE;
+
+ Base = 0;
+ EnumNumber = 1;
+
+ //
+ // Now see if the 1st batch of enumerated account Sids returned match those
+ // expected.
+ //
+
+ for(Index = 0; Index < CountReturned; Index++) {
+
+ SidFound = FALSE;
+
+ for (SearchIndex = 0; SearchIndex < 4; SearchIndex++) {
+
+ if (RtlEqualSid(
+ AccountSidInfo[SearchIndex].Sid,
+ EnumerationInformation[Index].Sid
+ )) {
+
+ if (AccountSidInfo[SearchIndex].SidFound) {
+
+ DbgPrint(
+ "Already found Sid with SearchIndex %d\n",
+ SearchIndex
+ );
+
+ } else {
+
+ AccountSidInfo[SearchIndex].SidFound = TRUE;
+ }
+
+ SidFound = TRUE;
+ break;
+ }
+ }
+
+ //
+ // If the enumerated Sid is not found, complain.
+ //
+
+ if (!SidFound) {
+
+ DbgPrint(
+ "Enumeration %d, index %d, Sid not found\n",
+ EnumNumber,
+ Index
+ );
+ }
+ }
+
+ Base += CountReturned;
+ EnumNumber++;
+ CountReturned = 0;
+
+ if (EnumerationInformationVoid != NULL) {
+
+ Status = LsaFreeMemory(EnumerationInformationVoid);
+ EnumerationInformationVoid = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Enumerate Accounts");
+ DbgPrint(" - LsaFreeMemory(EnumerationInformationVoid");
+ DbgPrint(" failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ PreferedMaximumLength = RtlLengthSid(PebblesSid) +
+ sizeof(LSA_ENUMERATION_INFORMATION) +
+ RtlLengthSid(DinoSid) +
+ sizeof(LSA_ENUMERATION_INFORMATION);
+
+ Status = LsaEnumerateAccounts(
+ PolicyHandle,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ EnumerationInformation =
+ (PLSA_ENUMERATION_INFORMATION) EnumerationInformationVoid;
+
+ if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {
+
+ DbgPrint("LSA RPC CT - Enumeration of last two accounts failed\n");
+ DbgPrint("0x%lx\n", Status);
+ goto EnumerateAccountsError;
+ }
+
+ if (CountReturned < 1 || CountReturned > 3) {
+
+ DbgPrint("LSA_RPC_CT - CountReturned invalid\n");
+ DbgPrint("expected 1 <= CountReturned <= 3\n");
+ DbgPrint("got %d\n", CountReturned);
+ goto EnumerateAccountsError;
+ }
+
+ //
+ // Now see if the 2nd batch of enumerated account Sids returned match those
+ // expected.
+ //
+
+ for(Index = 0; Index < CountReturned; Index++) {
+
+ SidFound = FALSE;
+
+ for (SearchIndex = 0; SearchIndex < 4; SearchIndex++) {
+
+ if (RtlEqualSid(
+ AccountSidInfo[SearchIndex].Sid,
+ EnumerationInformation[Index].Sid
+ )) {
+
+ if (AccountSidInfo[SearchIndex].SidFound) {
+
+ DbgPrint(
+ "Already found Sid with SearchIndex %d\n",
+ SearchIndex
+ );
+
+ } else {
+
+ AccountSidInfo[SearchIndex].SidFound = TRUE;
+ }
+
+ SidFound = TRUE;
+ break;
+ }
+ }
+
+ //
+ // If the enumerated Sid is not found, complain.
+ //
+
+ if (!SidFound) {
+
+ DbgPrint(
+ "Enumeration %d, index %d, Sid not found\n",
+ EnumNumber,
+ Index
+ );
+
+ goto EnumerateAccountsError;
+ }
+ }
+
+ if (EnumerationInformationVoid != NULL) {
+
+ Status = LsaFreeMemory(EnumerationInformationVoid);
+ EnumerationInformationVoid = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Enumerate Accounts");
+ DbgPrint(" - LsaFreeMemory(EnumerationInformationVoid");
+ DbgPrint(" failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // Bad Addresses passed to Lsa Enumerate Accounts API
+ //
+
+ if (Level == 2) {
+
+ Status = LsaEnumerateAccounts(
+ (LSA_HANDLE) BadAddress,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaEnumerateAccounts(
+ PolicyHandle,
+ (PLSA_ENUMERATION_HANDLE) BadAddress,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaEnumerateAccounts(
+ PolicyHandle,
+ &EnumerationContext,
+ BadEnumerationAddress,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaEnumerateAccounts(
+ PolicyHandle,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ BadCountReturnedAddress
+ );
+ }
+
+EnumerateAccountsFinish:
+
+ if (EnumerationInformationVoid != NULL) {
+
+ Status = LsaFreeMemory(EnumerationInformationVoid);
+ EnumerationInformationVoid = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Enumerate Accounts");
+ DbgPrint(" - LsaFreeMemory(EnumerationInformationVoid");
+ DbgPrint(" failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ return(BooleanStatus);
+
+EnumerateAccountsError:
+
+ goto EnumerateAccountsFinish;
+}
+
+
+BOOLEAN
+CtLsaAccountSystemAccess(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the set & query System Access of accounts in the LSA
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ BOOLEAN BooleanStatus = TRUE;
+
+
+ LSA_HANDLE AccountHandleGsaFred = NULL;
+ LSA_HANDLE AccountHandleSsaFred = NULL;
+ LSA_HANDLE AccountHandleCGsaFred = NULL;
+ LSA_HANDLE AccountHandleCSsaFred = NULL;
+ LSA_HANDLE InvalidHandle = NULL;
+
+ LSA_HANDLE PolicyHandle = NULL;
+
+ ULONG GetSystemAccessFred = 0;
+ ULONG SetSystemAccessFred = 0;
+ ACCESS_MASK ComplAccessRequiredGet;
+ ACCESS_MASK ComplAccessRequiredSet;
+
+ DbgPrint("[6] - Test Account System Access API\n");
+
+ //
+ // Open the Lsa Policy Object
+ //
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ GENERIC_READ,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle %d failed 0x%lx\n",
+ Index,
+ Status
+ );
+ }
+
+ //
+ // Open the Fred account for querying system access
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ FredSid,
+ ACCOUNT_VIEW,
+ &AccountHandleGsaFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Open Fred Acct for ACCOUNT_VIEW Access failed 0x%lx\n", Status);
+ return(FALSE);
+ }
+
+ GetSystemAccessFred = (ULONG) 0xffffffffL;
+
+ //
+ // Query the System Access flags for Fred
+ //
+
+ Status = LsaGetSystemAccessAccount(
+ AccountHandleGsaFred,
+ &GetSystemAccessFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - 1st LsaGetSystemAccess.. Fred for ACCOUNT_VIEW Access failed 0x%lx\n",
+ Status
+ );
+
+ goto AccountSystemAccessError;
+ }
+
+ //
+ // Since the system access flags have never been set, 0 should be returned.
+ //
+
+ if (GetSystemAccessFred != 0) {
+
+ DbgPrint(
+ "System Access Flags for Fred = 0x%lx, expected 0\n",
+ GetSystemAccessFred
+ );
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Open the Fred account for setting system access
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ FredSid,
+ ACCOUNT_ADJUST_SYSTEM_ACCESS,
+ &AccountHandleSsaFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Open Fred Acct for\n");
+ DbgPrint(" ACCOUNT_ADJUST_SYSTEM_ACCESS failed 0x%lx\n", Status);
+ goto AccountSystemAccessError;
+ }
+
+ //
+ // Now set the system access flags for Fred
+ //
+
+ SetSystemAccessFred = POLICY_MODE_ALL;
+
+ Status = LsaSetSystemAccessAccount(
+ AccountHandleSsaFred,
+ SetSystemAccessFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - LsaSetSystemAccessAccount(Fred) failed 0x%lx\n",
+ Status
+ );
+
+ goto AccountSystemAccessError;
+ }
+
+ //
+ // Now query the System Access flags for Fred again
+ //
+
+ GetSystemAccessFred = 0;
+
+ Status = LsaGetSystemAccessAccount(
+ AccountHandleGsaFred,
+ &GetSystemAccessFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - 1st LsaGetSystemAccess.. Fred for ACCOUNT_VIEW Access failed 0x%lx\n",
+ Status
+ );
+
+ goto AccountSystemAccessError;
+ }
+
+ //
+ // The returned System Access Flags should match those set.
+ //
+
+ if (GetSystemAccessFred != SetSystemAccessFred) {
+
+ DbgPrint("System Access Flags for Fred mismatch\n");
+ DbgPrint(
+ " - Set 0x%lx but returned 0x%lx\n",
+ SetSystemAccessFred,
+ GetSystemAccessFred
+ );
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Try invalid handle (NULL)
+ //
+
+ InvalidHandle = NULL;
+
+ Status = LsaGetSystemAccessAccount(
+ InvalidHandle,
+ &GetSystemAccessFred
+ );
+
+ if (Status != STATUS_INVALID_HANDLE) {
+
+ DbgPrint("Passing NULL handle to LsaGetSystemAccessAccount ");
+ DbgPrint(" gives incorrect error status code\n");
+
+ DbgPrint(
+ "Expected Ox%lx (STATUS_INVALID_HANDLE), got 0x%lx\n",
+ STATUS_INVALID_HANDLE,
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ InvalidHandle = NULL;
+
+ Status = LsaSetSystemAccessAccount(
+ InvalidHandle,
+ SetSystemAccessFred
+ );
+
+ if (Status != STATUS_INVALID_HANDLE) {
+
+ DbgPrint("Passing NULL handle to LsaSetSystemAccessAccount ");
+ DbgPrint(" gives incorrect error status code\n");
+
+ DbgPrint(
+ "Expected Ox%lx (STATUS_INVALID_HANDLE), got 0x%lx\n",
+ STATUS_INVALID_HANDLE,
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Try invalid handle (handle of another object type)
+ //
+
+ InvalidHandle = PolicyHandle;
+
+ Status = LsaGetSystemAccessAccount(
+ InvalidHandle,
+ &GetSystemAccessFred
+ );
+
+ if (Status != STATUS_INVALID_HANDLE) {
+
+ DbgPrint("Passing wrong handle type to LsaGetSystemAccessAccount ");
+ DbgPrint(" gives incorrect error status code\n");
+
+ DbgPrint(
+ "Expected Ox%lx (STATUS_INVALID_HANDLE), got 0x%lx\n",
+ STATUS_INVALID_HANDLE,
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ Status = LsaSetSystemAccessAccount(
+ InvalidHandle,
+ SetSystemAccessFred
+ );
+
+ if (Status != STATUS_INVALID_HANDLE) {
+
+ DbgPrint("Passing wrong handle type to LsaSetSystemAccessAccount ");
+ DbgPrint(" gives incorrect error status code\n");
+
+ DbgPrint(
+ "Expected Ox%lx (STATUS_INVALID_HANDLE), got 0x%lx\n",
+ STATUS_INVALID_HANDLE,
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Try invalid access types
+ //
+
+
+ //
+ // Open the Fred account for Complement of access needed to query
+ // system accesses.
+ //
+
+ ComplAccessRequiredGet = (ACCOUNT_ALL_ACCESS & ~(ACCOUNT_VIEW));
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ FredSid,
+ ComplAccessRequiredGet,
+ &AccountHandleCGsaFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LsaOpenAccount for complement of ACCOUNT_VIEW access failed 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // Now query the System Access flags for Fred. We should get
+ // STATUS_ACCESS_DENIED.
+ //
+
+ GetSystemAccessFred = 0;
+
+ Status = LsaGetSystemAccessAccount(
+ AccountHandleCGsaFred,
+ &GetSystemAccessFred
+ );
+
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint(
+ "LSA RPC CT - LsaGetSystemAccessAccount(Fred) returned 0x%lx\n",
+ Status
+ );
+
+ DbgPrint(
+ "expected 0x%lx (STATUS_ACCESS_DENIED)\n",
+ STATUS_ACCESS_DENIED
+ );
+
+ goto AccountSystemAccessError;
+ }
+ }
+
+ ComplAccessRequiredSet = (ACCOUNT_ALL_ACCESS & ~(ACCOUNT_ADJUST_SYSTEM_ACCESS));
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ FredSid,
+ ComplAccessRequiredSet,
+ &AccountHandleCSsaFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LsaOpenAccount for complement of ACCOUNT_ADJUST_SYSTEM_ACCESS\n"
+ );
+
+ DbgPrint("failed 0x%lx\n", Status);
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // Now query the System Access flags for Fred. We should get
+ // STATUS_ACCESS_DENIED.
+ //
+
+ GetSystemAccessFred = 0;
+ Status = LsaSetSystemAccessAccount(
+ AccountHandleCSsaFred,
+ SetSystemAccessFred
+ );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint(
+ "LSA RPC CT - LsaSetSystemAccessAccount(Fred) returned 0x%lx\n",
+ Status
+ );
+
+ DbgPrint(
+ "expected 0x%lx (STATUS_ACCESS_DENIED)\n",
+ STATUS_ACCESS_DENIED
+ );
+
+ goto AccountSystemAccessError;
+ }
+ }
+
+AccountSystemAccessFinish:
+
+ //
+ // Close all handles
+ //
+
+ if (AccountHandleGsaFred != NULL) {
+
+ Status = LsaClose(AccountHandleGsaFred);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaClose(AccountHandleGsaFred) failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ if (AccountHandleSsaFred != NULL) {
+
+ Status = LsaClose(AccountHandleSsaFred);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaClose(AccountHandleSsaFred) failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ if (AccountHandleCGsaFred != NULL) {
+
+ Status = LsaClose(AccountHandleCGsaFred);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaClose(AccountHandleCGsaFred failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ if (AccountHandleCSsaFred != NULL) {
+
+ Status = LsaClose(AccountHandleCSsaFred);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaClose(AccountHandleCSsaFred failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose(PolicyHandle);
+ }
+
+ return(BooleanStatus);
+
+AccountSystemAccessError:
+
+ BooleanStatus = FALSE;
+ goto AccountSystemAccessFinish;
+}
+
+BOOLEAN
+CtLsaAccountDelete(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests deletion of accounts. First, a simple test is
+ made to see if we can delete a freshly created account. Next, we
+ try to delete the accounts created by CtLsaAccountCreate.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any error.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("[7] - Test Account Deletion API\n");
+
+ //
+ // First try a simple test. Create a new account called Barney
+ // to be given DELETE access upon create. Then delete it.
+ //
+
+ //
+ // Create the Barney Account
+ //
+
+ DesiredAccessBarney = GENERIC_ALL;
+
+ Status = LsaCreateAccount(
+ PolicyHandle,
+ BarneySid,
+ DesiredAccessBarney,
+ &AccountHandleBarney
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Barney Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the Barney account
+ //
+
+ Status = LsaDelete( AccountHandleBarney );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Barney Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the Fred account. This should work since it was opened
+ // with GENERIC_ALL access.
+ //
+
+ Status = LsaDelete( AccountHandleFred );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Fred Acct failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the Wilma account. This should not work since it was opened
+ // with GENERIC_READ access.
+ //
+
+ if (DesiredAccessWilma != GENERIC_READ) {
+
+ DbgPrint("DesiredAccessWilma should be GENERIC_READ\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( AccountHandleWilma );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete Wilma Acct should have failed\n");
+ DbgPrint(" with STATUS_ACCESS_DENIED since handle specifies\n");
+ DbgPrint("GENERIC_READ access - got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to Wilma, open another handle with GENERIC_ALL
+ // access and try to delete the account again.
+ //
+
+ Status = LsaClose(AccountHandleWilma);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close Wilma account failed 0x%lx\n", Status);
+ }
+
+ AccountHandleWilmaOpen = NULL;
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ WilmaSid,
+ GENERIC_ALL,
+ &AccountHandleWilmaOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open Wilma Acct for GENERIC_ALL access failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaDelete( AccountHandleWilmaOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Wilma Acct failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Delete the Pebbles account. This should not work since it was opened
+ // with GENERIC_WRITE access which does not include DELETE.
+ //
+
+ if (DesiredAccessPebbles != GENERIC_WRITE) {
+
+ DbgPrint("DesiredAccessPebbles should be GENERIC_WRITE\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( AccountHandlePebbles );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete Pebbles Acct should have failed\n");
+ DbgPrint(" with STATUS_ACCESS_DENIED since handle specifies\n");
+ DbgPrint("GENERIC_READ access - got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to Pebbles and open another handle with DELETE
+ // access and try to delete the account again.
+ //
+
+ Status = LsaClose(AccountHandlePebbles);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close Pebbles account failed 0x%lx\n", Status);
+ }
+
+ AccountHandlePebblesOpen = NULL;
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ PebblesSid,
+ DELETE,
+ &AccountHandlePebblesOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open Pebbles Acct for DELETE access failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaDelete( AccountHandlePebblesOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Pebbles Acct failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ //
+ // Delete the Dino account. This should not work since it was opened
+ // with GENERIC_EXECUTE access.
+ //
+
+ if (DesiredAccessDino != GENERIC_EXECUTE) {
+
+ DbgPrint("DesiredAccessDino should be GENERIC_EXECUTE\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( AccountHandleDino );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete Dino Acct failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to Dino, open another handle with DELETE
+ // access and try to delete it again.
+ //
+
+ Status = LsaClose(AccountHandleDino);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close Dino account failed 0x%lx\n", Status);
+ }
+
+ AccountHandleDinoOpen = NULL;
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ DinoSid,
+ DELETE,
+ &AccountHandleDinoOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open Dino Acct for DELETE access failed 0x%lx\n",
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ Status = LsaDelete( AccountHandleDinoOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Dino Acct failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+BOOLEAN
+CtLsaTrustedDomainObject(
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the Lsa TrustedDomain Object API.
+
+Arguments:
+
+ TrustedClient - Specifies whether Trusted Client variations
+ are to be run additionally. NOTE: These can only be run
+ with ctlsarpc -lsainit... substituted for lsass.exe
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("********************************************************\n");
+ DbgPrint("LSA RPC CT - Test Lsa Trusted Domain Object API\n");
+ DbgPrint("********************************************************\n");
+
+ //
+ // Open a handle to the LSA
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ GENERIC_ALL,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Lsa Open failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Test TrustedDomain creation. Abandon TrustedDomain testing if error.
+ //
+
+ if (!CtLsaTrustedDomainCreate()) {
+
+ return FALSE;
+ }
+
+ //
+ // Test TrustedDomain open and close. Abandon TrustedDomain testing if error.
+ //
+
+ if (!CtLsaTrustedDomainOpenClose()) {
+
+ return FALSE;
+ }
+
+ //
+ // Test TrustedDomain Set and Query Info
+ //
+
+ if (!CtLsaTrustedDomainSetQueryInfo()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Test TrustedDomain enumeration.
+ //
+
+ if (!CtLsaTrustedDomainEnumeration()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Test TrustedDomain deletion.
+ //
+
+ if (!CtLsaTrustedDomainDelete()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ return BooleanStatus;
+
+ DBG_UNREFERENCED_PARAMETER( TrustedClient );
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainCreate(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LSA API that Create TrustedDomains.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ UNICODE_STRING BedrockADomainName;
+ UNICODE_STRING BedrockBDomainName;
+ UNICODE_STRING BedrockCDomainName;
+ UNICODE_STRING BedrockDDomainName;
+
+ DbgPrint("[1] - Test TrustedDomain Creation API\n");
+
+ RtlInitUnicodeString( &BedrockADomainName, L"BedrockA" );
+ RtlInitUnicodeString( &BedrockBDomainName, L"BedrockB" );
+ RtlInitUnicodeString( &BedrockCDomainName, L"BedrockC" );
+ RtlInitUnicodeString( &BedrockDDomainName, L"BedrockD" );
+
+ //
+ // Create the new Trusted Domains in the LSA Database.
+ //
+
+ DesiredAccessBedrockA = GENERIC_ALL;
+ CtLsaTrustedDomainSetInfo(
+ BedrockADomainSid,
+ &BedrockADomainName,
+ &TrustedDomainInfoBedrockA
+ );
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ &TrustedDomainInfoBedrockA,
+ DesiredAccessBedrockA,
+ &TrustedDomainHandleBedrockA
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_OBJECT_NAME_EXISTS) {
+
+ DbgPrint("LSA RPC CT - Create BedrockA Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+ }
+
+ DesiredAccessBedrockB = GENERIC_READ;
+ CtLsaTrustedDomainSetInfo(
+ BedrockBDomainSid,
+ &BedrockBDomainName,
+ &TrustedDomainInfoBedrockB
+ );
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ &TrustedDomainInfoBedrockB,
+ DesiredAccessBedrockB,
+ &TrustedDomainHandleBedrockB
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create BedrockB Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessBedrockC = GENERIC_WRITE;
+ CtLsaTrustedDomainSetInfo(
+ BedrockCDomainSid,
+ &BedrockCDomainName,
+ &TrustedDomainInfoBedrockC
+ );
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ &TrustedDomainInfoBedrockC,
+ DesiredAccessBedrockC,
+ &TrustedDomainHandleBedrockC
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create BedrockC Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessBedrockD = GENERIC_EXECUTE;
+ CtLsaTrustedDomainSetInfo(
+ BedrockDDomainSid,
+ &BedrockDDomainName,
+ &TrustedDomainInfoBedrockD
+ );
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ &TrustedDomainInfoBedrockD,
+ DesiredAccessBedrockD,
+ &TrustedDomainHandleBedrockD
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create BedrockD Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Close the TrustedDomains just created
+ //
+
+ Status = LsaClose(TrustedDomainHandleBedrockA);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new BedrockA TrustedDomain failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ Status = LsaClose(TrustedDomainHandleBedrockB);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new BedrockB TrustedDomain failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ Status = LsaClose(TrustedDomainHandleBedrockC);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Close new BedrockC TrustedDomain failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaClose(TrustedDomainHandleBedrockD);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new BedrockD TrustedDomain failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainOpenClose(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LSA API that open and close TrustedDomains.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus;
+ PLSA_HANDLE BadHandleAddress;
+
+ DbgPrint("[2] - Test TrustedDomain Open and Close API\n");
+
+ //
+ // Open the TrustedDomains created by CtLsaTrustedDomainCreate
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockADomainSid,
+ DesiredAccessBedrockA,
+ &TrustedDomainHandleBedrockA
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open BedrockA Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockBDomainSid,
+ DesiredAccessBedrockB,
+ &TrustedDomainHandleBedrockB
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open BedrockB Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockCDomainSid,
+ DesiredAccessBedrockC,
+ &TrustedDomainHandleBedrockC
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Pebbles Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Open BedrockD TrustedDomain.
+ // This is a spare call and should be changed in some way.
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockDDomainSid,
+ DesiredAccessBedrockD,
+ &TrustedDomainHandleBedrockD
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open BedrockD Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Now open each TrustedDomain a second time so we have 2 handles to each
+ // TrustedDomain.
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockADomainSid,
+ DesiredAccessBedrockA,
+ &TrustedDomainHandleBedrockA2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open BedrockA Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockBDomainSid,
+ DesiredAccessBedrockB,
+ &TrustedDomainHandleBedrockB2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open BedrockB Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockCDomainSid,
+ DesiredAccessBedrockC,
+ &TrustedDomainHandleBedrockC2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open BedrockC Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockDDomainSid,
+ DesiredAccessBedrockD,
+ &TrustedDomainHandleBedrockD2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Open BedrockD Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Close the 2nd handles to the TrustedDomains
+ //
+
+ Status = LsaClose( TrustedDomainHandleBedrockA2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close BedrockA Trusted Domain hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( TrustedDomainHandleBedrockB2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close BedrockB Trusted Domain hdl2 failed 0x%lx\n",Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( TrustedDomainHandleBedrockC2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close BedrockC Trusted Domain hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( TrustedDomainHandleBedrockD2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close BedrockD Trusted Domain hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ BooleanStatus = TRUE;
+
+ //
+ // Try to create BedrockA again. We should get STATUS_OBJECT_NAME_COLLISION
+ //
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ &TrustedDomainInfoBedrockA,
+ GENERIC_ALL,
+ &TrustedDomainHandleBedrockA3
+ );
+
+ if (Status != STATUS_OBJECT_NAME_COLLISION) {
+
+ DbgPrint("LSA RPC CT - Create BedrockA Trusted Domain when BedrockA exists\n");
+ DbgPrint("and LSA_OBJECT_CREATE disposition specified\n");
+ DbgPrint("Expected 0x%lx (STATUS_OBJECT_NAME_COLLISION)\n",
+ STATUS_OBJECT_NAME_COLLISION);
+ DbgPrint("Got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ return BooleanStatus;
+
+ if (Level == 2) {
+
+ //
+ // LsaOpenTrustedDomain with bad addresses
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockADomainSid,
+ DesiredAccessBedrockA,
+ BadHandleAddress
+ );
+
+ //
+ // Lsa Close with bad address
+ //
+
+ Status = LsaClose( (LSA_HANDLE) BadAddress );
+ }
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainSetQueryInfo(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaSetInformationTrustedDomain and
+ LsaQueryInfoTrustedDomain API.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ BOOLEAN - TRUE if test is successful, else FALSE
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("[3] - Test TrustedDomain Set and Query Info API\n");
+
+ BooleanStatus &= CtLsaTrustedDomainAccountInfo();
+ BooleanStatus &= CtLsaTrustedDomainControllersInfo();
+ BooleanStatus &= CtLsaTrustedDomainPosixOffsetInfo();
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainAccountInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ TRUSTED_DOMAIN_NAME_INFO TrustedDomainNameInfo;
+
+ //
+ // Setup sample Trusted Account Name Info
+ //
+
+ RtlInitUnicodeString(
+ &TrustedDomainNameInfo.Name,
+ L"TRUSTEDACCOUNT1"
+ );
+
+ BooleanStatus = CtLsaTrustedDomainSetQuerySub(
+ TrustedDomainNameInformation,
+ &TrustedDomainNameInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainControllersInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG Controller;
+ UNICODE_STRING Names[CT_TRUSTED_CONTROLLER_COUNT];
+
+ PWSTR Strings[CT_TRUSTED_CONTROLLER_COUNT] = {
+
+ L"\\CONTROLLERA",
+ L"\\CONTROLLERB",
+ L"\\CONTROLLERC",
+ L"\\CONTROLLERD"
+
+ };
+
+ TRUSTED_CONTROLLERS_INFO TrustedControllersInfo;
+
+ //
+ // Setup sample Trusted Controllers Info
+ //
+
+ TrustedControllersInfo.Entries = CT_TRUSTED_CONTROLLER_COUNT;
+
+ for (Controller = 0;
+ Controller < CT_TRUSTED_CONTROLLER_COUNT;
+ Controller++) {
+
+ RtlInitUnicodeString( &Names[Controller], Strings[Controller] );
+ }
+
+ TrustedControllersInfo.Names = Names;
+
+ BooleanStatus = CtLsaTrustedDomainSetQuerySub(
+ TrustedControllersInformation,
+ &TrustedControllersInfo
+ );
+
+
+ return(BooleanStatus);
+}
+
+
+
+BOOLEAN
+CtLsaTrustedDomainPosixOffsetInfo()
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ TRUSTED_POSIX_OFFSET_INFO TrustedPosixOffsetInfo;
+
+ //
+ // Setup sample Trusted Posix Offset Info
+ //
+
+ TrustedPosixOffsetInfo.Offset = CT_TRUSTED_POSIX_OFFSET;
+
+ BooleanStatus = CtLsaTrustedDomainSetQuerySub(
+ TrustedPosixOffsetInformation,
+ &TrustedPosixOffsetInfo
+ );
+
+ return(BooleanStatus);
+}
+
+
+
+BOOLEAN
+CtLsaTrustedDomainSetQuerySub(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID TrustedDomainInformation
+ )
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE TrustedDomainHandleSet = NULL;
+ LSA_HANDLE TrustedDomainHandleQuery = NULL;
+ PVOID ReturnedTrustedDomainInformation;
+ ACCESS_MASK DesiredAccess;
+ LSA_HANDLE PolicyHandle = NULL;
+
+ ACCESS_MASK RequiredAccessSetTrustedDomain[TrustedPosixOffsetInformation+1] =
+ {
+ 0,
+ 0, // TrustedDomainNameInformation can't be set
+ TRUSTED_SET_CONTROLLERS,
+ TRUSTED_SET_POSIX
+ };
+
+
+ ACCESS_MASK RequiredAccessQueryTrustedDomain[TrustedPosixOffsetInformation+1] =
+ {
+ 0,
+ TRUSTED_QUERY_DOMAIN_NAME,
+ TRUSTED_QUERY_CONTROLLERS,
+ TRUSTED_QUERY_POSIX
+ };
+
+ //
+ // Open a handle to the LSA
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ GENERIC_ALL,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Lsa Open failed 0x%lx\n", Status);
+
+ goto TrustedSetQuerySubError;
+ }
+
+ DbgPrint("...Testing information class %d\n", InformationClass);
+
+ //
+ // Open a handle to the TrustedDomain Object with the access required for
+ // setting the given TrustedDomain Information Class. If the Class
+ // is not settable via LsaSetInformationTrustedDomain(), ie requires
+ // a trusted client, the call is expected to fail even if we open
+ // the TrustedDomain Object with GENERIC_ALL.
+ //
+
+ if (CtLsaTrustedDomainSetInfoAllowed(InformationClass)) {
+
+ DesiredAccess = RequiredAccessSetTrustedDomain[InformationClass];
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockADomainSid,
+ DesiredAccess,
+ &TrustedDomainHandleSet
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - TrustedDomain object open handle failed 0x%lx\n",
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Set the TrustedDomain Information for the specified class
+ //
+
+ try {
+
+ Status = LsaSetInformationTrustedDomain(
+ TrustedDomainHandleSet,
+ InformationClass,
+ TrustedDomainInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaTrustedDomainSetQuerySub\n");
+ DbgPrint(" within LsaSetInformationTrustedDomain call 1\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto TrustedSetQuerySubError;
+ }
+
+ //
+ // If setting of the InformationClass via LsaSetInformationTrustedDomain()
+ // is allowed, we expect STATUS_SUCCESS, otherwise we expect
+ // STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaTrustedDomainSetInfoAllowed(InformationClass)) {
+
+ if (!NT_SUCCESS(Status)) {
+
+ BooleanStatus = FALSE;
+
+ DbgPrint(
+ "LsaSetInformationTrustedDomain - Class %d failed 0x%lx\n",
+ InformationClass,
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be set via LsaSetInformationTrustedDomain
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("LsaSetInformationTrustedDomain - Attempt to set ");
+ DbgPrint("prohibited Information Class should have\n");
+ DbgPrint(
+ "failed 0x%lx\n (STATUS_INVALID_PARAMETER)",
+ STATUS_INVALID_PARAMETER
+ );
+ DbgPrint("instead returned 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ Status = LsaClose(TrustedDomainHandleSet);
+ TrustedDomainHandleSet = NULL;
+
+ //
+ // Now open a handle to the TrustedDomain Object with the access required for
+ // querying the given TrustedDomain Information Class. If the Class
+ // is not queryable via LsaQueryInformationTrustedDomain(), ie requires
+ // a trusted client, the call is expected to fail even if we open
+ // the TrustedDomain Object with GENERIC_ALL.
+ //
+
+ if (CtLsaTrustedDomainQueryInfoAllowed(InformationClass)) {
+
+ DesiredAccess = RequiredAccessQueryTrustedDomain[InformationClass];
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockADomainSid,
+ DesiredAccess,
+ &TrustedDomainHandleQuery
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - TrustedDomain object open handle failed 0x%lx\n",
+ Status
+ );
+
+ goto TrustedSetQuerySubError;
+ }
+
+ //
+ // Now query the TrustedDomain Information set
+ //
+
+ try {
+
+ Status = LsaQueryInfoTrustedDomain(
+ TrustedDomainHandleQuery,
+ InformationClass,
+ &ReturnedTrustedDomainInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaTrustedDomainSetQuerySub\n");
+ DbgPrint(" within LsaQueryInformationTrustedDomain call 1\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto TrustedSetQuerySubError;
+ }
+
+ //
+ // If querying of the InformationClass via LsaQueryInformationTrustedDomain()
+ // is allowed, we expect STATUS_SUCCESS, otherwise we expect
+ // STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaTrustedDomainQueryInfoAllowed(InformationClass)) {
+
+ if (!NT_SUCCESS(Status)) {
+
+ BooleanStatus = FALSE;
+
+ DbgPrint(
+ "LsaQueryInformationTrustedDomain - Class %d failed 0x%lx\n",
+ InformationClass,
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be queried via LsaQueryInformationTrustedDomain
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("LsaQueryInformationTrustedDomain - Attempt to query ");
+ DbgPrint("prohibited Information Class should have\n");
+ DbgPrint(
+ "failed 0x%lx\n (STATUS_INVALID_PARAMETER)",
+ STATUS_INVALID_PARAMETER
+ );
+ DbgPrint("instead returned 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If set and query are both allowed for the Information Class, they
+ // should both have worked. In this case, compare the returned TrustedDomain
+ // information with that set
+ //
+
+ if (CtLsaTrustedDomainSetInfoAllowed(InformationClass) &&
+ CtLsaTrustedDomainQueryInfoAllowed(InformationClass)) {
+
+ BooleanStatus &= CtLsaTrustedDomainInfoClassCompare(
+ InformationClass,
+ TrustedDomainInformation,
+ ReturnedTrustedDomainInformation
+ );
+ }
+
+ if (ReturnedTrustedDomainInformation != NULL) {
+
+ Status = LsaFreeMemory(ReturnedTrustedDomainInformation);
+ ReturnedTrustedDomainInformation = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Query Info Trusted Domain");
+ DbgPrint(" - LsaFreeMemory(ReturnedTrustedDomainInformation)");
+ DbgPrint(" failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ Status = LsaClose(TrustedDomainHandleQuery);
+ TrustedDomainHandleQuery = NULL;
+
+ //
+ // Now open a handle to the TrustedDomain Object with all accesses except the
+ // access required for setting the given TrustedDomain Information Class.
+ // If the TrustedDomain Information Class cannot be set via public API,
+ // open the handle instead with TRUSTED_ALL_ACCESS.
+ //
+
+ if (CtLsaTrustedDomainSetInfoAllowed(InformationClass)) {
+
+ DesiredAccess =
+ (TRUSTED_ALL_ACCESS & ~RequiredAccessSetTrustedDomain[InformationClass]);
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockADomainSid,
+ DesiredAccess,
+ &TrustedDomainHandleSet
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - TrustedDomain object open handle failed 0x%lx\n",
+ Status
+ );
+
+ goto TrustedSetQuerySubError;
+ }
+
+ //
+ // Attempt to set the TrustedDomain Information for the specified class
+ // using the handle opened above. This handle has either the
+ // complement of the accesses required (if the class is settable
+ // via LsaSetInformationTrustedDomain()) or TrustedDomain_ALL_ACCESS. In the
+ // former case, we should get STATUS_ACCESS_DENIED back from
+ // LsaSetInformationTrustedDomain(), in the latter case, STATUS_INVALID_PARAMETER.
+ //
+
+ try {
+
+ Status = LsaSetInformationTrustedDomain(
+ TrustedDomainHandleSet,
+ InformationClass,
+ TrustedDomainInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaTrustedDomainSetQuerySub\n");
+ DbgPrint(" within LsaSetInformationTrustedDomain call 2\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // If setting of the InformationClass via LsaSetInformationTrustedDomain()
+ // is allowed, we expect STATUS_ACCESS_DENIED since we specified the
+ // complement of the access required (relative to TrustedDomain_ALL_ACCESS).
+ // Otherwise we expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaTrustedDomainSetInfoAllowed(InformationClass)) {
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LsaSetInformationTrustedDomain Class");
+
+ DbgPrint(
+ " %d access mask 0x%lx should have failed 0x%lx (STATUS_ACCESS_DENIED)\n",
+ InformationClass,
+ DesiredAccess,
+ STATUS_ACCESS_DENIED
+ );
+
+ DbgPrint(
+ "since the required access for setting info is 0x%lx\n",
+ RequiredAccessSetTrustedDomain[InformationClass]
+ );
+
+ DbgPrint(" but instead returned 0x%lx\n", Status);
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be set via LsaSetInformationTrustedDomain
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("LsaSetInformationTrustedDomain - Attempt to set ");
+ DbgPrint("prohibited Information Class should have\n");
+ DbgPrint(
+ "failed 0x%lx\n (STATUS_INVALID_PARAMETER)",
+ STATUS_INVALID_PARAMETER
+ );
+ DbgPrint("instead returned 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ Status = LsaClose(TrustedDomainHandleSet);
+ TrustedDomainHandleSet = NULL;
+
+ //
+ // Now open a handle to the TrustedDomain Object with all accesses except the
+ // access required for querying the given TrustedDomain Information Class.
+ // If the TrustedDomain Information Class cannot be queried via public API,
+ // open the handle instead with TrustedDomain_ALL_ACCESS.
+ //
+
+ if (CtLsaTrustedDomainQueryInfoAllowed(InformationClass)) {
+
+ DesiredAccess =
+ (TRUSTED_ALL_ACCESS & ~RequiredAccessQueryTrustedDomain[InformationClass]);
+
+ } else {
+
+ DesiredAccess = GENERIC_ALL;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockADomainSid,
+ DesiredAccess,
+ &TrustedDomainHandleQuery
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - TrustedDomain object open handle failed 0x%lx\n",
+ Status
+ );
+
+ goto TrustedSetQuerySubError;
+ }
+
+ try {
+
+ //
+ // Now query the TrustedDomain Information set. This should fail
+ // STATUS_ACCESS_DENIED
+ //
+
+ Status = LsaQueryInfoTrustedDomain(
+ TrustedDomainHandleQuery,
+ InformationClass,
+ &ReturnedTrustedDomainInformation
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ DbgPrint("Lsa CT RPC - Access Violation: CtLsaTrustedDomainSetQuerySub\n");
+ DbgPrint(" within LsaQueryInformationTrustedDomain call 2\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // If querying of the InformationClass via LsaQueryInformationTrustedDomain()
+ // is allowed, we expect STATUS_ACCESS_DENIED since we specified the
+ // complement of the access required (relative to TrustedDomain_ALL_ACCESS).
+ // Otherwise we expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (CtLsaTrustedDomainQueryInfoAllowed(InformationClass)) {
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LsaQueryInfoTrustedDomain Class");
+
+ DbgPrint(
+ " %d access mask 0x%lx should have failed 0x%lx (STATUS_ACCESS_DENIED)\n",
+ InformationClass,
+ DesiredAccess,
+ STATUS_ACCESS_DENIED
+ );
+
+ DbgPrint(
+ "since the required access for querying info is 0x%lx\n",
+ RequiredAccessQueryTrustedDomain[InformationClass]
+ );
+
+ DbgPrint(" but instead returned 0x%lx\n", Status);
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Information Class may not be queried via LsaQueryInformationTrustedDomain
+ // We expect STATUS_INVALID_PARAMETER.
+ //
+
+ if (Status != STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("LsaQueryInfoTrustedDomain - Attempt to query ");
+ DbgPrint("prohibited Information Class should have\n");
+ DbgPrint(
+ "failed 0x%lx\n (STATUS_INVALID_PARAMETER)",
+ STATUS_INVALID_PARAMETER
+ );
+ DbgPrint("instead returned 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ Status = LsaClose(TrustedDomainHandleQuery);
+ TrustedDomainHandleQuery = NULL;
+
+TrustedSetQuerySubFinish:
+
+ if (ReturnedTrustedDomainInformation != NULL) {
+
+ Status = LsaFreeMemory(ReturnedTrustedDomainInformation);
+ ReturnedTrustedDomainInformation = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Query Info Trusted Domain");
+ DbgPrint(" - LsaFreeMemory(ReturnedTrustedDomainInformation)");
+ DbgPrint(" failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ }
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose(PolicyHandle);
+ }
+
+ return(BooleanStatus);
+
+TrustedSetQuerySubError:
+
+ BooleanStatus = FALSE;
+ goto TrustedSetQuerySubFinish;
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainInfoClassCompare(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID TrustedDomainInformation1,
+ IN PVOID TrustedDomainInformation2
+ )
+
+/*++
+
+Routine Description:
+
+ This function compares two sets of TrustedDomain Information for a known
+ Information Class.
+
+Arguments:
+
+ InformationClass - Specifies a TrustedDomain Information Class
+
+ TrustedDomainInformation1 - First comparand. TrustedDomain Information for the
+ given information class.
+
+
+ TrustedDomainInformation2 - Second comparand. TrustedDomain Information for the
+ given information class.
+
+Return Values:
+
+ BOOLEAN - TRUE if TrustedDomain information sets are equal , else FALSE
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG Controller;
+ ULONG Entries;
+
+// PTRUSTED_DOMAIN_NAME_INFO TrustedDomainNameInfo1;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo1;
+ PTRUSTED_POSIX_OFFSET_INFO TrustedPosixOffsetInfo1;
+
+// PTRUSTED_DOMAIN_NAME_INFO TrustedDomainNameInfo2;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo2;
+ PTRUSTED_POSIX_OFFSET_INFO TrustedPosixOffsetInfo2;
+
+ //
+ // Switch on Information Class
+ //
+
+ switch (InformationClass) {
+
+ case TrustedDomainNameInformation:
+
+ /* This test will be enabled for trusted callers only
+
+ TrustedDomainNameInfo1 = (PTRUSTED_DOMAIN_NAME_INFO) TrustedDomainInfo1;
+ TrustedDomainNameInfo2 = (PTRUSTED_DOMAIN_NAME_INFO) TrustedDomainInfo2;
+
+ if (!RtlEqualUnicodeString(
+ &TrustedDomainNameInfo1,
+ &TrustedDomainNameInfo2,
+ FALSE
+ )) {
+
+ DbgPrint("TrustedDomainNameInfo - Name mismatch\n");
+ BooleanStatus = FALSE;
+ }
+
+ */
+ break;
+
+ case TrustedControllersInformation:
+
+ TrustedControllersInfo1 = (PTRUSTED_CONTROLLERS_INFO) TrustedDomainInformation1;
+ TrustedControllersInfo2 = (PTRUSTED_CONTROLLERS_INFO) TrustedDomainInformation2;
+
+ //
+ // First compare the number of entries
+ //
+
+ if (TrustedControllersInfo1->Entries !=
+ TrustedControllersInfo2->Entries) {
+
+ DbgPrint("TrustedControllersInfo - Entries mismatch\n");
+ DbgPrint(
+ ".. expected %d, got %d\n",
+ TrustedControllersInfo1->Entries,
+ TrustedControllersInfo2->Entries
+ );
+ BooleanStatus = FALSE;
+ }
+
+ Entries = TrustedControllersInfo1->Entries;
+
+ //
+ // Now compare each of the Trusted Controller names
+ //
+
+ for (Controller = 0; Controller < Entries; Controller++) {
+
+ if (!RtlEqualUnicodeString(
+ &TrustedControllersInfo1->Names[Controller],
+ &TrustedControllersInfo2->Names[Controller],
+ FALSE
+ )) {
+
+ DbgPrint("Trusted Controller Name mismatch\n");
+ DbgPrint(".. name number %d\n", Controller);
+ DbgPrint("No further names compared\n");
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+
+ break;
+
+ case TrustedPosixOffsetInformation:
+
+ TrustedPosixOffsetInfo1 = (PTRUSTED_POSIX_OFFSET_INFO) TrustedDomainInformation1;
+ TrustedPosixOffsetInfo2 = (PTRUSTED_POSIX_OFFSET_INFO) TrustedDomainInformation2;
+
+ //
+ // Compare the Posix Offset with the value set.
+ //
+
+ if (TrustedPosixOffsetInfo1->Offset != TrustedPosixOffsetInfo2->Offset) {
+
+ DbgPrint("Trusted Posix Offset Info - Mismatch on Offset\n");
+
+ DbgPrint(
+ ".. expected %d, got %d\n",
+ TrustedPosixOffsetInfo1->Offset,
+ TrustedPosixOffsetInfo2->Offset
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ default:
+
+ BooleanStatus = FALSE;
+
+ DbgPrint(
+ "Bug in test program - Invalid Information Class %d\n",
+ InformationClass
+ );
+
+ break;
+ }
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainQueryInfoAllowed(
+ IN TRUSTED_INFORMATION_CLASS InformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether querying of a TrustedDomain Information Class
+ is allowed via LsaQueryInformationTrustedDomain. Note that all TrustedDomain
+ Information Classes may be queried via (to be implemented) trusted
+ clients to private API.
+
+Arguments:
+
+ InformationClass - The TrustedDomain Information Class to be checked (assumed
+ valid).
+
+Return Value:
+
+ BOOLEAN - TRUE if the information class can be queried via
+ LsaQueryInformationTrustedDomain() else FALSE.
+
+--*/
+
+{
+ //
+ // Range check the InformationClass parameter.
+ //
+
+ if ((InformationClass < TrustedDomainNameInformation) ||
+ (InformationClass > TrustedPosixOffsetInformation)) {
+
+ DbgPrint("WARNING! CtLsaTrustedDomainQueryInfoAllowed:\n");
+
+ DbgPrint(
+ " InformationClass %d is invalid - check caller\n",
+ InformationClass
+ );
+
+ return(FALSE);
+ }
+
+ //
+ // Currently all Information Classes may be queried via
+ // LsaQueryInfoTrustedDomain(). Place any future restrictions
+ // on what may be queried here.
+ //
+
+ return(TRUE);
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainSetInfoAllowed(
+ IN TRUSTED_INFORMATION_CLASS InformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether setting of a TrustedDomain Information Class
+ is allowed via LsaSetInformationTrustedDomain. Note that all TrustedDomain
+ Information Classes may be set via (to be implemented) trusted
+ clients to private API.
+
+Arguments:
+
+ InformationClass - The TrustedDomain Information Class to be checked (assumed
+ valid).
+
+Return Value:
+
+ BOOLEAN - TRUE idf the information class can be set via
+ LsaSetInformationTrustedDomain() else FALSE.
+
+--*/
+
+{
+ //
+ // Range check the InformationClass parameter.
+ //
+
+ if ((InformationClass < TrustedDomainNameInformation) ||
+ (InformationClass > TrustedPosixOffsetInformation)) {
+
+ DbgPrint("WARNING! CtLsaTrustedDomainSetInfoAllowed:\n");
+
+ DbgPrint(
+ " InformationClass %d is invalid - check caller\n",
+ InformationClass
+ );
+
+ return(FALSE);
+ }
+
+ //
+ // Check if this Information Class can be set via
+ // LsaSetInformationTrustedDomain().
+ //
+
+ if (InformationClass == TrustedDomainNameInformation) {
+
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainEnumeration(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the enumeration of TrustedDomains in the LSA
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG Base = 0;
+ ULONG CountReturned = 0;
+ ULONG PreferedMaximumLength;
+ ULONG EnumerationContext = 0;
+ ULONG EnumNumber = 0;
+ ULONG EnumIndex;
+ ULONG Index;
+ PLSA_TRUST_INFORMATION EnumerationInformation;
+ PVOID EnumerationInformationVoid = NULL;
+ PVOID *BadEnumerationAddress = NULL;
+ PULONG BadCountReturnedAddress = NULL;
+ ULONG CallNumber;
+
+ CT_LSA_SINGLE_CALL_ENUM_INFO EnumerationInformations[ 20 ];
+
+ DbgPrint("[4] - Test TrustedDomain Enumeration API\n");
+
+ //
+ // Enumerate the TrustedDomains in the Lsa. Set the Prefered Maximum
+ // Length od data returned to a low value to cause as many subsequent
+ // calls to the LsaEnumerateTrustedDomains API as possible.
+ //
+
+ PreferedMaximumLength = 1;
+
+ CallNumber = 1;
+
+ Status = STATUS_SUCCESS;
+
+ while(NT_SUCCESS(Status)) {
+
+ Status = LsaEnumerateTrustedDomains(
+ PolicyHandle,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ EnumerationInformations[ EnumNumber ].EnumInfoReturned =
+ EnumerationInformationVoid;
+ EnumerationInformations[ EnumNumber ].CountReturned = CountReturned;
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ CallNumber++;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumeration of TrustedDomains failed\n"
+ "Call number %d to LsaEnumerateTrustedDomains returned 0x%lx\n",
+ CallNumber,
+ Status
+ );
+ return FALSE;
+ }
+ }
+
+ TrustedDomainSidInfo[0].Sid = BedrockADomainSid;
+ TrustedDomainSidInfo[1].Sid = BedrockBDomainSid;
+ TrustedDomainSidInfo[3].Sid = BedrockCDomainSid;
+ TrustedDomainSidInfo[2].Sid = BedrockDDomainSid;
+
+ TrustedDomainSidInfo[0].SidFound = FALSE;
+ TrustedDomainSidInfo[1].SidFound = FALSE;
+ TrustedDomainSidInfo[2].SidFound = FALSE;
+ TrustedDomainSidInfo[3].SidFound = FALSE;
+
+ //
+ // Now see if we found all of the TrustedDomain Sids we were looking for.
+ //
+
+ for (EnumIndex = 0; EnumIndex < EnumNumber; EnumIndex++) {
+
+ CountReturned = EnumerationInformations[ EnumIndex ].CountReturned;
+ EnumerationInformation = (PLSA_TRUST_INFORMATION)
+ EnumerationInformations[ EnumIndex ].EnumInfoReturned;
+
+ for( Index = 0; Index < CountReturned; Index++ ) {
+
+ SidFound = FALSE;
+
+ for ( SearchIndex = 0; SearchIndex < 4; SearchIndex++ ) {
+
+ if (RtlEqualSid(
+ TrustedDomainSidInfo[SearchIndex].Sid,
+ EnumerationInformation[Index].Sid
+ )) {
+
+ if (TrustedDomainSidInfo[SearchIndex].SidFound) {
+
+ DbgPrint(
+ "Already found Sid with SearchIndex %d\n",
+ SearchIndex
+ );
+
+ } else {
+
+ TrustedDomainSidInfo[SearchIndex].SidFound = TRUE;
+ }
+
+ SidFound = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // Now check that all of the Sids were found.
+ //
+
+ for (SearchIndex = 0; SearchIndex < 4; SearchIndex++ ) {
+
+ if (!TrustedDomainSidInfo[ SearchIndex ].SidFound ) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumeration of TrustedDomains failed\n"
+ "Trusted Domain Sid number %d was not found",
+ SearchIndex
+ );
+
+ BooleanStatus = FALSE;
+
+ break;
+ }
+ }
+
+ if (!BooleanStatus) {
+
+ return(FALSE);
+ }
+
+ //
+ // Bad Addresses passed to Lsa Enumerate TrustedDomains API
+ //
+
+ if (Level == 2) {
+
+ Status = LsaEnumerateTrustedDomains(
+ (LSA_HANDLE) BadAddress,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaEnumerateTrustedDomains(
+ PolicyHandle,
+ (PLSA_ENUMERATION_HANDLE) BadAddress,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaEnumerateTrustedDomains(
+ PolicyHandle,
+ &EnumerationContext,
+ BadEnumerationAddress,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaEnumerateTrustedDomains(
+ PolicyHandle,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ BadCountReturnedAddress
+ );
+ }
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaTrustedDomainDelete(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests deletion of TrustedDomains. First, a simple test is
+ made to see if we can delete a freshly created TrustedDomain. Next, we
+ try to delete the TrustedDomains created by CtLsaTrustedDomainCreate.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any error.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ UNICODE_STRING BedrockEDomainName;
+ PSID BedrockEDomainSid = BedrockDomainSid;
+
+ DbgPrint("[5] - Test TrustedDomain Deletion API\n");
+
+ //
+ // First try a simple test. Create a new TrustedDomain called BedrockE
+ // to be given DELETE access upon create. Then delete it.
+ //
+
+ RtlInitUnicodeString( &BedrockEDomainName, L"BedrockE" );
+
+ DesiredAccessBedrockE = DELETE;
+
+ CtLsaTrustedDomainSetInfo(
+ BedrockEDomainSid,
+ &BedrockEDomainName,
+ &TrustedDomainInfoBedrockE
+ );
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ &TrustedDomainInfoBedrockE,
+ DesiredAccessBedrockE,
+ &TrustedDomainHandleBedrockE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create BedrockE Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the BedrockE TrustedDomain
+ //
+
+ Status = LsaDelete( TrustedDomainHandleBedrockE );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockE Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the BedrockA TrustedDomain. This should work since it was opened
+ // with GENERIC_ALL access.
+ //
+
+ Status = LsaDelete( TrustedDomainHandleBedrockA );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockA Trusted Domain failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the BedrockB TrustedDomain. This should not work since it was opened
+ // with GENERIC_READ access.
+ //
+
+ if (DesiredAccessBedrockB != GENERIC_READ) {
+
+ DbgPrint("DesiredAccessBedrockB should be GENERIC_READ\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( TrustedDomainHandleBedrockB );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockB Trusted Domain should have failed\n");
+ DbgPrint(" with STATUS_ACCESS_DENIED since handle specifies\n");
+ DbgPrint("GENERIC_READ access - got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to BedrockB, open another handle with GENERIC_ALL
+ // access and try to delete the TrustedDomain again.
+ //
+
+ Status = LsaClose(TrustedDomainHandleBedrockB);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close BedrockB TrustedDomain failed 0x%lx\n", Status);
+ }
+
+ TrustedDomainHandleBedrockBOpen = NULL;
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockBDomainSid,
+ GENERIC_ALL,
+ &TrustedDomainHandleBedrockBOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open BedrockB Trusted Domain for GENERIC_ALL access failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaDelete( TrustedDomainHandleBedrockBOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockB Trusted Domain failed 0x%lx\n", Status);
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Delete the BedrockC TrustedDomain. This should not work since it was opened
+ // with GENERIC_WRITE access which does not include DELETE.
+ //
+
+ if (DesiredAccessBedrockC != GENERIC_WRITE) {
+
+ DbgPrint("DesiredAccessBedrockC should be GENERIC_WRITE\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( TrustedDomainHandleBedrockC );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockC Trusted Domain should have failed\n");
+ DbgPrint(" with STATUS_ACCESS_DENIED since handle specifies\n");
+ DbgPrint("GENERIC_READ access - got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to BedrockC and open another handle with DELETE
+ // access and try to delete the TrustedDomain again.
+ //
+
+ Status = LsaClose(TrustedDomainHandleBedrockC);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close BedrockC TrustedDomain failed 0x%lx\n", Status);
+ }
+
+ TrustedDomainHandleBedrockCOpen = NULL;
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockCDomainSid,
+ DELETE,
+ &TrustedDomainHandleBedrockCOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open BedrockC Trusted Domain for DELETE access failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaDelete( TrustedDomainHandleBedrockCOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockC Trusted Domain failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+ //
+ // Delete the BedrockD TrustedDomain. This should not work since it was opened
+ // with GENERIC_EXECUTE access.
+ //
+
+ if (DesiredAccessBedrockD != GENERIC_EXECUTE) {
+
+ DbgPrint("DesiredAccessBedrockD should be GENERIC_EXECUTE\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( TrustedDomainHandleBedrockD );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockD Trusted Domain failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to BedorckD, open another handle with DELETE
+ // access and try to delete it again.
+ //
+
+ Status = LsaClose(TrustedDomainHandleBedrockD);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close BedrockD TrustedDomain failed 0x%lx\n", Status);
+ }
+
+ TrustedDomainHandleBedrockDOpen = NULL;
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ BedrockDDomainSid,
+ DELETE,
+ &TrustedDomainHandleBedrockDOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open BedrockD Trusted Domain for DELETE access failed 0x%lx\n",
+ Status
+ );
+ BooleanStatus = FALSE;
+ }
+
+ Status = LsaDelete( TrustedDomainHandleBedrockDOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete BedrockD Trusted Domain failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+VOID
+CtLsaTrustedDomainSetInfo(
+ IN PSID DomainSid,
+ IN PUNICODE_STRING DomainName,
+ OUT PLSA_TRUST_INFORMATION TrustedDomainInfo
+ )
+
+{
+ TrustedDomainInfo->Name = *DomainName;
+ TrustedDomainInfo->Sid = DomainSid;
+
+}
+
+BOOLEAN
+CtLsaSecretObject(
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the Lsa Secret Object API.
+
+Arguments:
+
+ TrustedClient - Specifies whether Trusted Client variations
+ are to be run additionally. NOTE: These can only be run
+ with ctlsarpc -lsainit... substituted for lsass.exe
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ BOOLEAN BooleanStatusDelete = TRUE;
+ BOOLEAN SecretsCreated = FALSE;
+
+ DbgPrint("********************************************************\n");
+ DbgPrint("LSA RPC CT - Test Lsa Secret Object API\n");
+ DbgPrint("********************************************************\n");
+
+ //
+ // Open a handle to the LSA
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ Status = LsaOpenPolicy(
+ SystemName,
+ &ObjectAttributes,
+ GENERIC_ALL,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Lsa Open failed 0x%lx\n", Status);
+ goto SecretObjectError;
+ }
+
+ //
+ // Cleanup secrets from last run
+ //
+
+ if (!CtLsaSecretCleanup()) {
+
+ DbgPrint("LSA RPC CT - Pre-test Secret cleanup failed\n");
+ }
+
+ //
+ // Test Secret creation. Abandon Secret testing if error.
+ //
+
+ if (!CtLsaSecretCreate()) {
+
+ goto SecretObjectError;
+ }
+
+ SecretsCreated = TRUE;
+
+ //
+ // Test Secret open and close. Abandon Secret testing if error.
+ //
+
+ if (!CtLsaSecretOpenClose()) {
+
+ goto SecretObjectError;
+ }
+
+ //
+ // Test Secret Set and Query Value
+ //
+
+ if (!CtLsaSecretSetQueryValue()) {
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Test Secret Enumeration (Only Trusted Callers can do this)
+ //
+
+ if (TrustedClient) {
+
+ if (!CtLsaSecretEnumeration()) {
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // Test Secret Set Times (only Trusted Callers can do this)
+ //
+
+ if (TrustedClient) {
+
+ if (!CtLsaSecretSetTimes()) {
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // Test Secret deletion.
+ //
+
+ if (!CtLsaSecretDelete()) {
+
+ BooleanStatusDelete = FALSE;
+ goto SecretObjectError;
+ }
+
+ SecretsCreated = FALSE;
+
+SecretObjectFinish:
+
+ //
+ // Cleanup secrets from last run
+ //
+
+ if (!CtLsaSecretCleanup()) {
+
+ DbgPrint("LSA RPC CT - Pre-test Secret cleanup failed\n");
+ }
+
+ return BooleanStatus;
+
+SecretObjectError:
+
+ BooleanStatus = FALSE;
+ goto SecretObjectFinish;
+}
+
+
+BOOLEAN
+CtLsaSecretCreate(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LSA API that Create Secrets.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ DbgPrint("[1] - Test Secret Creation API\n");
+
+ //
+ // Set up Secret information for Secrets to be created.
+ //
+
+ Status = CtSecretSetInfo(
+ "Fred",
+ "Fred secret current value",
+ "Fred secret old value",
+ &SecretInfoFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - CtSecretSetInfo for Fred failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = CtSecretSetInfo(
+ "Wilma",
+ "Wilma secret current value",
+ "Wilma secret old value",
+ &SecretInfoWilma
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - CtSecretSetInfo for Wilma failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = CtSecretSetInfo(
+ "Dino",
+ "Dino secret current value",
+ "Dino secret old value",
+ &SecretInfoDino
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - CtSecretSetInfo for Dino failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = CtSecretSetInfo(
+ "Pebbles",
+ "Pebbles secret current value",
+ "Pebbles secret old value",
+ &SecretInfoPebbles
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - CtSecretSetInfo for Pebbles failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ //
+ // Create the new Secrets in the LSA Database.
+ //
+
+ DesiredAccessFred = GENERIC_ALL;
+
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &(SecretInfoFred.SecretName),
+ DesiredAccessFred,
+ &SecretHandleFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Fred Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessWilma = GENERIC_READ;
+
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &(SecretInfoWilma.SecretName),
+ DesiredAccessWilma,
+ &SecretHandleWilma
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Wilma Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessPebbles = GENERIC_WRITE;
+
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &(SecretInfoPebbles.SecretName),
+ DesiredAccessPebbles,
+ &SecretHandlePebbles
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Pebbles Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ DesiredAccessDino = GENERIC_EXECUTE;
+
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &(SecretInfoDino.SecretName),
+ DesiredAccessDino,
+ &SecretHandleDino
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Dino Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Close the Secrets just created
+ //
+
+ Status = LsaClose(SecretHandleFred);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new Fred Secret failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ Status = LsaClose(SecretHandleWilma);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new Wilma Secret failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ Status = LsaClose(SecretHandlePebbles);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Close new Pebbles Secret failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaClose(SecretHandleDino);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close new Dino Secret failed 0x%lx\n",
+ Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+CtLsaSecretOpenClose(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LSA API that open and close Secrets.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus;
+ PLSA_HANDLE BadSecretHandleAddress = NULL;
+
+ DbgPrint("[2] - Test Secret Open and Close API\n");
+
+ //
+ // Open the Secrets created by CtLsaSecretCreate
+ //
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoFred.SecretName),
+ DesiredAccessFred,
+ &SecretHandleFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Fred Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoWilma.SecretName),
+ DesiredAccessWilma,
+ &SecretHandleWilma
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Wilma Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoPebbles.SecretName),
+ DesiredAccessPebbles,
+ &SecretHandlePebbles
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Pebbles Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoDino.SecretName),
+ DesiredAccessDino,
+ &SecretHandleDino
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 1st Open Dino Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Now open each Secret a second time so we have 2 handles to each
+ // Secret.
+ //
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoFred.SecretName),
+ DesiredAccessFred,
+ &SecretHandleFred2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open Fred Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoWilma.SecretName),
+ DesiredAccessWilma,
+ &SecretHandleWilma2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open Wilma Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoPebbles.SecretName),
+ DesiredAccessPebbles,
+ &SecretHandlePebbles2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open Pebbles Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoDino.SecretName),
+ DesiredAccessDino,
+ &SecretHandleDino2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - 2nd Open Dino Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Close the 2nd handles to the Secrets
+ //
+
+ Status = LsaClose( SecretHandleFred2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Fred Secret hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( SecretHandleWilma2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Wilma Secret hdl2 failed 0x%lx\n",Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( SecretHandlePebbles2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Pebbles Secret hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ Status = LsaClose( SecretHandleDino2 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Close Dino Secret hdl2 failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ BooleanStatus = TRUE;
+
+ //
+ // Try to create Fred again. We
+ // should get STATUS_OBJECT_NAME_COLLISION
+ //
+
+ DesiredAccessFred = GENERIC_ALL;
+
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &(SecretInfoFred.SecretName),
+ DesiredAccessFred,
+ &SecretHandleFred3
+ );
+
+ if (Status != STATUS_OBJECT_NAME_COLLISION) {
+
+ DbgPrint("LSA RPC CT - Create Fred Secret when Fred exists\n");
+ DbgPrint("Expected 0x%lx (STATUS_OBJECT_NAME_COLLISION)\n",
+ STATUS_OBJECT_NAME_COLLISION);
+ DbgPrint("Got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ return BooleanStatus;
+
+ if (Level == 2) {
+
+ //
+ // LsaOpenSecret with bad addresses
+ //
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ (PUNICODE_STRING) BadAddress,
+ DesiredAccessFred,
+ &SecretHandleFred
+ );
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoFred.SecretName),
+ DesiredAccessFred,
+ BadSecretHandleAddress
+ );
+ }
+
+ return(BooleanStatus);
+}
+
+BOOLEAN
+CtLsaSecretSetQueryValue(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the Setting and Querying of Secret Values.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any error.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ LARGE_INTEGER FredCurrentValueSetTime;
+ LARGE_INTEGER FredOldValueSetTime;
+ LARGE_INTEGER FredCurrentValueSetTime2;
+ LARGE_INTEGER FredOldValueSetTime2;
+
+ DbgPrint("[3] - Test Secret Set and Query Value API\n");
+
+ //
+ // Query Secret Values for Fred before any set.
+ //
+
+ Status = LsaQuerySecret(
+ SecretHandleFred,
+ &(SecretInfoFred.ReturnedCurrentValue),
+ &FredCurrentValueSetTime,
+ &(SecretInfoFred.ReturnedOldValue),
+ &FredOldValueSetTime
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaQuerySecret for Fred before values set failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Since the Secret Values for Fred have never been set, we
+ // should have NULLs for them
+ //
+
+ if (SecretInfoFred.ReturnedCurrentValue != NULL) {
+
+ DbgPrint("LsaQuerySecret for Fred did not return\n");
+ DbgPrint("NULL for CurrentValue when Current Value\n");
+ DbgPrint("has never been set\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (SecretInfoFred.ReturnedOldValue != NULL) {
+
+ DbgPrint("LsaQuerySecret for Fred did not return\n");
+ DbgPrint("NULL for OldValue when Old Value\n");
+ DbgPrint("has never been set\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Set Secret Values for Fred.
+ //
+
+ Status = LsaSetSecret(
+ SecretHandleFred,
+ &(SecretInfoFred.CurrentValue),
+ &(SecretInfoFred.OldValue)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaSetSecret for Fred failed 0x%lx\n", Status);
+ return(FALSE);
+ }
+
+ //
+ // Query Secret Values for Fred after Set.
+ //
+
+ Status = LsaQuerySecret(
+ SecretHandleFred,
+ &(SecretInfoFred.ReturnedCurrentValue),
+ &FredCurrentValueSetTime2,
+ &(SecretInfoFred.ReturnedOldValue),
+ &FredOldValueSetTime2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaQuerySecret for Fred failed 0x%lx\n", Status);
+ return(FALSE);
+ }
+
+ //
+ // Compare returned Current Value of Fred Secret with original.
+ //
+
+ if (!RtlEqualUnicodeString(
+ &(SecretInfoFred.CurrentValue),
+ SecretInfoFred.ReturnedCurrentValue,
+ FALSE
+ )) {
+
+ DbgPrint("LsaSetSecret - LsaQuerySecret value mismatch\n");
+ DbgPrint("... returned Current Value of Fred Secret incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Free the memory for the returned Current Value of Fred
+ //
+
+ Status = LsaFreeMemory(SecretInfoFred.ReturnedCurrentValue);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaFreeMemory for Fred Secret Query 1 Current Value\n");
+ DbgPrint("failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ SecretInfoFred.ReturnedCurrentValue = NULL;
+
+ //
+ // Compare returned Old Value of Fred Secret with original.
+ //
+
+ if (!RtlEqualUnicodeString(
+ &(SecretInfoFred.OldValue),
+ SecretInfoFred.ReturnedOldValue,
+ FALSE
+ )) {
+
+ DbgPrint("LsaSetSecret - LsaQuerySecret value mismatch\n");
+ DbgPrint("... returned Old Value of Fred Secret incorrect\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Free the memory for the returned Old Value of Fred
+ //
+
+ Status = LsaFreeMemory(SecretInfoFred.ReturnedOldValue);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaFreeMemory for Fred Secret Query 1 Old Value\n");
+ DbgPrint("failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ SecretInfoFred.ReturnedOldValue = NULL;
+
+
+ //
+ // Query Secret Values for Fred (no creation timestamp info wanted).
+ //
+
+ Status = LsaQuerySecret(
+ SecretHandleFred,
+ &(SecretInfoFred.ReturnedCurrentValue),
+ NULL,
+ &(SecretInfoFred.ReturnedOldValue),
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaQuerySecret for Fred failed 0x%lx\n", Status);
+ return(FALSE);
+ }
+
+ //
+ // Compare Returned Current Value of Fred Secret with original.
+ //
+
+ if (!RtlEqualUnicodeString(
+ &(SecretInfoFred.CurrentValue),
+ SecretInfoFred.ReturnedCurrentValue,
+ FALSE
+ )) {
+
+ DbgPrint("LsaSetSecret - LsaQuerySecret value mismatch\n");
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Free the memory for the returned Current Value of Fred
+ //
+
+ Status = LsaFreeMemory(SecretInfoFred.ReturnedCurrentValue);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaFreeMemory for Fred Secret Query 2 Current Value\n");
+ DbgPrint("failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ SecretInfoFred.ReturnedCurrentValue = NULL;
+
+ //
+ // Compare returned Old Value of Fred Secret with original.
+ //
+
+ if (!RtlEqualUnicodeString(
+ &(SecretInfoFred.OldValue),
+ SecretInfoFred.ReturnedOldValue,
+ FALSE
+ )) {
+
+ DbgPrint("LsaSetSecret - LsaQuerySecret value mismatch\n");
+ DbgPrint("... returned Old Value of Fred Secret incorrect\n");
+ BooleanStatus = FALSE;
+ }
+ //
+ // Free the memory for the returned Old Value of Fred
+ //
+
+ Status = LsaFreeMemory(SecretInfoFred.ReturnedOldValue);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaFreeMemory for Fred Secret Query 2 Old Value\n");
+ DbgPrint("failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ SecretInfoFred.ReturnedOldValue = NULL;
+
+
+ //
+ // Set NULL Secret Values for Fred.
+ //
+
+ Status = LsaSetSecret(
+ SecretHandleFred,
+ NULL,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaSetSecret (NULL values) for Fred failed 0x%lx\n", Status);
+ return(FALSE);
+ }
+
+ //
+ // Query Secret Values for Fred after Set.
+ //
+
+ Status = LsaQuerySecret(
+ SecretHandleFred,
+ &(SecretInfoFred.ReturnedCurrentValue),
+ &FredCurrentValueSetTime2,
+ &(SecretInfoFred.ReturnedOldValue),
+ &FredOldValueSetTime2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LsaQuerySecret (NULL Values) for Fred failed 0x%lx\n", Status);
+ return(FALSE);
+ }
+
+ //
+ // Verify that values returned are NULL.
+ //
+
+ if (SecretInfoFred.ReturnedCurrentValue != NULL) {
+
+ DbgPrint("LsaQuerySecret - Returned Current Value should be NULL\n");
+ BooleanStatus = FALSE;
+ }
+
+ if (SecretInfoFred.ReturnedOldValue != NULL) {
+
+ DbgPrint("LsaQuerySecret - Returned Old Value should be NULL\n");
+ BooleanStatus = FALSE;
+ }
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaSecretEnumeration(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the enumeration of Secrets in the LSA. Note that
+ Secret enumeration may only be performed by Trusted Clients.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any failures.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG Base = 0;
+ ULONG CountReturned = 0;
+ ULONG PreferedMaximumLength;
+ ULONG EnumerationContext = 0;
+ ULONG EnumNumber = 0;
+ ULONG EnumIndex;
+ ULONG Index;
+ PUNICODE_STRING EnumerationInformation;
+ PVOID EnumerationInformationVoid = NULL;
+ PVOID *BadEnumerationAddress = NULL;
+ PULONG BadCountReturnedAddress = NULL;
+ LSAPR_HANDLE TrustedPolicyHandle = NULL;
+
+ CT_LSA_SINGLE_CALL_ENUM_INFO EnumerationInformations[ 20 ];
+
+ DbgPrint("[4] - Test Secret Enumeration (Trusted Callers) API\n");
+
+ //
+ // First open a Trusted Handle to the LSA.
+ //
+
+ Status = LsaIOpenPolicyTrusted( &TrustedPolicyHandle );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumeration of Secrets failed\n"
+ "LsaIOpenPolicyTrusted call %d returned 0x%lx\n",
+ "... no more enumerations attempted\n",
+ EnumNumber,
+ Status
+ );
+
+ goto EnumerateSecretsError;
+ }
+
+ //
+ // Enumerate the Secrets in the Lsa. Set the Prefered Maximum
+ // Length od data returned to a low value to cause as many subsequent
+ // calls to the LsaEnumerateSecrets API as possible.
+ //
+
+ PreferedMaximumLength = 1;
+
+ EnumNumber = 0;
+
+ Status = STATUS_SUCCESS;
+
+ while(NT_SUCCESS(Status)) {
+
+ Status = LsaIEnumerateSecrets(
+ TrustedPolicyHandle,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ EnumerationInformations[ EnumNumber ].EnumInfoReturned =
+ EnumerationInformationVoid;
+ EnumerationInformations[ EnumNumber ].CountReturned = CountReturned;
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumeration of Secrets failed\n"
+ "LsaIEnumerateSecrets call %d returned 0x%lx\n",
+ "... no more enumerations attempted\n",
+ EnumNumber,
+ Status
+ );
+
+ break;
+ }
+ }
+
+ EnumNumber++;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumeration of Secrets failed\n"
+ "Call number %d to LsaEnumerateSecrets returned 0x%lx\n",
+ EnumNumber,
+ Status
+ );
+
+ goto EnumerateSecretsError;
+ }
+ }
+
+ SecretNameInfo[0].Name = FredName;
+ SecretNameInfo[1].Name = WilmaName;
+ SecretNameInfo[3].Name = PebblesName;
+ SecretNameInfo[2].Name = DinoName;
+
+ SecretNameInfo[0].NameFound = FALSE;
+ SecretNameInfo[1].NameFound = FALSE;
+ SecretNameInfo[2].NameFound = FALSE;
+ SecretNameInfo[3].NameFound = FALSE;
+
+ //
+ // Scan all of the enumeration information returned and lookup
+ // each Secret Name returned in the SecretNameInfo array. If the
+ // name is found, set the NameFound flag in the array entry. Also,
+ // check for duplicates.
+ //
+
+ for (EnumIndex = 0; EnumIndex < EnumNumber; EnumIndex++) {
+
+ CountReturned = EnumerationInformations[ EnumIndex ].CountReturned;
+ EnumerationInformation = (PUNICODE_STRING)
+ EnumerationInformations[ EnumIndex ].EnumInfoReturned;
+
+ for( Index = 0; Index < CountReturned; Index++ ) {
+
+ //
+ // Search for this Secret Name in the SecretNameInfo array.
+ // If found, set NameFound, else ignore (there may be other
+ // Secret objects not involved in this test).
+ //
+
+ for ( SearchIndex = 0; SearchIndex < 4; SearchIndex++ ) {
+
+ if (RtlEqualUnicodeString(
+ &SecretNameInfo[SearchIndex].Name,
+ &EnumerationInformation[Index],
+ TRUE
+ )) {
+
+ if (SecretNameInfo[SearchIndex].NameFound) {
+
+ DbgPrint(
+ "Already found Sid with SearchIndex %d\n",
+ SearchIndex
+ );
+
+ } else {
+
+ SecretNameInfo[SearchIndex].NameFound = TRUE;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // We've scanned all of the returned Secret Names. Now check that all
+ // of the Names were found.
+ //
+
+ for (SearchIndex = 0; SearchIndex < 4; SearchIndex++ ) {
+
+ if ( !SecretNameInfo[ SearchIndex ].NameFound ) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumeration of Secrets failed\n"
+ "Secret number %d was not found",
+ SearchIndex
+ );
+
+ BooleanStatus = FALSE;
+
+ break;
+ }
+ }
+
+ if (!BooleanStatus) {
+
+ return(FALSE);
+ }
+
+ //
+ // Bad Addresses passed to Lsa Enumerate Secrets API
+ //
+
+ if (Level == 2) {
+
+ Status = LsaIEnumerateSecrets(
+ (LSA_HANDLE) BadAddress,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaIEnumerateSecrets(
+ PolicyHandle,
+ (PLSA_ENUMERATION_HANDLE) BadAddress,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaIEnumerateSecrets(
+ PolicyHandle,
+ &EnumerationContext,
+ BadEnumerationAddress,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ Status = LsaIEnumerateSecrets(
+ PolicyHandle,
+ &EnumerationContext,
+ &EnumerationInformationVoid,
+ PreferedMaximumLength,
+ BadCountReturnedAddress
+ );
+ }
+
+EnumerateSecretsFinish:
+
+ //
+ // If necessary, close the TrustedPolicyHandle.
+ //
+
+ if (TrustedPolicyHandle != NULL) {
+
+ Status = LsarClose( &TrustedPolicyHandle );
+
+ TrustedPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumeration of Secrets failed\n"
+ "LsaClose on TrustedPolicyHandle returned 0x%lx\n",
+ Status
+ );
+
+ goto EnumerateSecretsError;
+ }
+ }
+
+ return(BooleanStatus);
+
+EnumerateSecretsError:
+
+ BooleanStatus = FALSE;
+ goto EnumerateSecretsFinish;
+}
+
+
+BOOLEAN
+CtLsaSecretSetTimes(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the setting of Secret Current and Old Value
+ Set Times. Only Trusted Callers can do this.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any error.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ LSAPR_HANDLE TrustedPolicyHandle = NULL;
+ LSAPR_HANDLE TrustedSecretHandleFred = NULL;
+ LARGE_INTEGER FredCurrentValueSetTime;
+ LARGE_INTEGER FredOldValueSetTime;
+ LARGE_INTEGER SystemTime;
+
+ DbgPrint("[5] - Test Secret Set Times API\n");
+
+ //
+ // First open a Trusted Handle to the LSA.
+ //
+
+ Status = LsaIOpenPolicyTrusted( &TrustedPolicyHandle );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "LsaIOpenPolicyTrusted returned 0x%lx\n",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+
+ //
+ // Now open a handle to Fred. This handle will be trusted by
+ // inheritance from the TrustedPolicyhandle.
+ //
+
+ Status = LsarOpenSecret(
+ TrustedPolicyHandle,
+ (PLSAPR_UNICODE_STRING) &FredName,
+ (ACCESS_MASK) 0,
+ &TrustedSecretHandleFred
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "LsarOpenSecret call returned 0x%lx\n",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+
+ //
+ // Now query the current time
+ //
+
+ Status = NtQuerySystemTime(&SystemTime);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "NtQuerySystemTime() returned 0x%lx\n",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+
+ //
+ // Now change the times that the Fred Secret Values were set.
+ //
+
+ Status = LsaISetTimesSecret( TrustedSecretHandleFred, &SystemTime, &SystemTime );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "LsaiSetTimesSecret() returned 0x%lx\n",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+
+ //
+ // Now Query Secret Values for Fred.
+ //
+
+
+ FredCurrentValueSetTime.LowPart = 0;
+ FredCurrentValueSetTime.HighPart = 0;
+
+ FredOldValueSetTime.LowPart = 0;
+ FredOldValueSetTime.HighPart = 0;
+
+ Status = LsarQuerySecret(
+ TrustedSecretHandleFred,
+ NULL,
+ &FredCurrentValueSetTime,
+ NULL,
+ &FredOldValueSetTime
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "LsarQuerySecret for Fred returned 0x%lx\n",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+
+ //
+ // Now compare the returned Current and Old Value Set Times with
+ // the SystemTime.
+ //
+
+ if (!( FredCurrentValueSetTime.QuadPart == SystemTime.QuadPart )) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "Mismatch on CurrentValueSetTime\n ",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+
+ if (!( FredOldValueSetTime.QuadPart == SystemTime.QuadPart )) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "Mismatch on OldValueSetTime\n ",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+
+SetTimesSecretFinish:
+
+ //
+ // If necessary, close the Fred Secret Handle.
+ //
+
+ if (TrustedSecretHandleFred != NULL) {
+
+ Status = LsarClose( &TrustedSecretHandleFred );
+
+ TrustedSecretHandleFred = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "LsaClose on TrustedFredSecretHandle returned 0x%lx\n",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+ }
+
+ //
+ // If necessary, close the TrustedPolicyHandle.
+ //
+
+ if (TrustedPolicyHandle != NULL) {
+
+ Status = LsarClose( &TrustedPolicyHandle );
+
+ TrustedPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set Times for Secret Values failed\n"
+ "LsaClose on TrustedPolicyHandle returned 0x%lx\n",
+ Status
+ );
+
+ goto SetTimesSecretError;
+ }
+ }
+
+ return(BooleanStatus);
+
+SetTimesSecretError:
+
+ BooleanStatus = FALSE;
+ goto SetTimesSecretFinish;
+}
+
+
+BOOLEAN
+CtLsaSecretDelete(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests deletion of Secrets. First, a simple test is
+ made to see if we can delete a freshly created Secret. Next, we
+ try to delete the Secrets created by CtLsaSecretCreate.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any error.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("[6] - Test Secret Deletion API\n");
+
+ //
+ // First try a simple test. Create a new Secret called Barney
+ // to be given DELETE access upon create. Then delete it.
+ //
+
+ //
+ // Set up Secret information for Barney Secret to be created.
+ //
+
+ Status = CtSecretSetInfo(
+ "Barney",
+ "Barney secret current value",
+ "Barney secret old value",
+ &SecretInfoBarney
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - CtSecretSetInfo for Barney failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ //
+ // Create the Barney Secret
+ //
+
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &(SecretInfoBarney.SecretName),
+ DELETE,
+ &SecretHandleBarney
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Create Barney Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the Barney Secret
+ //
+
+ Status = LsaDelete( SecretHandleBarney );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Barney Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the Fred Secret. This should work since it was opened
+ // with GENERIC_ALL access.
+ //
+
+ if (DesiredAccessFred != GENERIC_ALL) {
+
+ DbgPrint("DesiredAccessFred should be GENERIC_ALL \n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( SecretHandleFred );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Fred Secret failed 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Delete the Wilma Secret. This should not work since it was opened
+ // with GENERIC_READ access.
+ //
+
+ if (DesiredAccessWilma != GENERIC_READ) {
+
+ DbgPrint("DesiredAccessWilma should be GENERIC_READ\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( SecretHandleWilma );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete Wilma Secret should have failed\n");
+ DbgPrint(" with STATUS_ACCESS_DENIED since handle specifies\n");
+ DbgPrint("GENERIC_READ access - got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to Wilma, open another handle with GENERIC_ALL
+ // access and try to delete the Secret again.
+ //
+
+ Status = LsaClose(SecretHandleWilma);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close Secret account failed 0x%lx\n", Status);
+ }
+
+ SecretHandleWilmaOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoWilma.SecretName),
+ DELETE,
+ &SecretHandleWilmaOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open Wilma Secret for DELETE failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaDelete( SecretHandleWilmaOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Wilma Secret failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Delete the Pebbles Secret. This should not work since it was opened
+ // with GENERIC_WRITE access which does not include DELETE.
+ //
+
+ if (DesiredAccessPebbles != GENERIC_WRITE) {
+
+ DbgPrint("DesiredAccessPebbles should be GENERIC_WRITE\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( SecretHandlePebbles );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete Pebbles Secret should have failed\n");
+ DbgPrint(" with STATUS_ACCESS_DENIED since handle specifies\n");
+ DbgPrint("GENERIC_WRITE access - got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to Pebbles, open another handle with DELETE
+ // access and try to delete the Secret again.
+ //
+
+ Status = LsaClose(SecretHandlePebbles);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close Pebbles Secret account failed 0x%lx\n", Status);
+ }
+
+ SecretHandlePebblesOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoPebbles.SecretName),
+ DELETE,
+ &SecretHandlePebblesOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open Pebbles Secret for DELETE failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaDelete( SecretHandlePebblesOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Wilma Secret failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Delete the Dino Secret. This should not work since it was opened
+ // with GENERIC_EXECUTE access.
+ //
+
+ if (DesiredAccessDino != GENERIC_EXECUTE) {
+
+ DbgPrint("DesiredAccessDino should be GENERIC_EXECUTE\n");
+ DbgPrint("Bug in this test program\n");
+ return FALSE;
+ }
+
+ Status = LsaDelete( SecretHandleDino );
+
+ if (Status != STATUS_ACCESS_DENIED) {
+
+ DbgPrint("LSA RPC CT - Delete Dino Secret should have failed\n");
+ DbgPrint(" with STATUS_ACCESS_DENIED since handle specifies\n");
+ DbgPrint("GENERIC_WRITE access - got 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now close the handle to Dino, open another handle with DELETE
+ // access and try to delete the Secret again.
+ //
+
+ Status = LsaClose(SecretHandleDino);
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Close Dino Secret account failed 0x%lx\n", Status);
+ }
+
+ SecretHandleDinoOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoDino.SecretName),
+ DELETE,
+ &SecretHandleDinoOpen
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Open Dino Secret for DELETE failed 0x%lx\n",
+ Status
+ );
+ return FALSE;
+ }
+
+ Status = LsaDelete( SecretHandleDinoOpen );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Delete Dino Secret failed 0x%lx\n", Status);
+ BooleanStatus = FALSE;
+ }
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaSecretCleanup(
+ )
+
+/*++
+
+Routine Description:
+
+ This function cleans up any secrets created by the Secret tests.
+ No failures are reported.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, FALSE if any error.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ LSA_HANDLE SecretHandleBarneyOpen = NULL;
+
+ //
+ // Close the handle to Barney (if any), open another handle with DELETE
+ // access and try to delete the Secret again.
+ //
+
+ if (SecretHandleBarney != NULL) {
+
+ Status = LsaClose(SecretHandleBarney);
+ SecretHandleBarney = NULL;
+ }
+
+ //
+ // Open new handle to Barney with DELETE access
+ //
+
+ SecretHandleBarneyOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoBarney.SecretName),
+ DELETE,
+ &SecretHandleBarneyOpen
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsaDelete( SecretHandleBarneyOpen );
+
+ SecretHandleBarneyOpen = NULL;
+ }
+
+ //
+ // Close the handle to Fred (if any), open another handle with DELETE
+ // access and try to delete the Secret again.
+ //
+
+ if (SecretHandleFred != NULL) {
+
+ Status = LsaClose(SecretHandleFred);
+ SecretHandleFred = NULL;
+ }
+
+ //
+ // Open new handle to Fred with DELETE access
+ //
+
+ SecretHandleFredOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoFred.SecretName),
+ DELETE,
+ &SecretHandleFredOpen
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsaDelete( SecretHandleFredOpen );
+
+ SecretHandleFredOpen = NULL;
+ }
+
+ //
+ // Close the handle to Wilma (if any), open another handle with DELETE
+ // access and try to delete the Secret again.
+ //
+
+ if (SecretHandleWilma != NULL) {
+
+ Status = LsaClose(SecretHandleWilma);
+ SecretHandleWilma = NULL;
+ }
+
+ //
+ // Open new handle to Wilma with DELETE access
+ //
+
+ SecretHandleWilmaOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoWilma.SecretName),
+ DELETE,
+ &SecretHandleWilmaOpen
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsaDelete( SecretHandleWilmaOpen );
+
+ SecretHandleWilmaOpen = NULL;
+ }
+
+ //
+ // Close the handle to Pebbles (if any), open another handle with DELETE
+ // access and try to delete the Secret again.
+ //
+
+ if (SecretHandlePebbles != NULL) {
+
+ Status = LsaClose(SecretHandlePebbles);
+ SecretHandlePebbles = NULL;
+ }
+
+ //
+ // Open new handle to Pebbles with DELETE access
+ //
+
+ SecretHandlePebblesOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoPebbles.SecretName),
+ DELETE,
+ &SecretHandlePebblesOpen
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsaDelete( SecretHandlePebblesOpen );
+
+ SecretHandlePebblesOpen = NULL;
+ }
+
+ //
+ // Close the handle to Dino (if any), open another handle with DELETE
+ // access and try to delete the Secret again.
+ //
+
+ if (SecretHandleDino != NULL) {
+
+ Status = LsaClose(SecretHandleDino);
+ SecretHandleDino = NULL;
+ }
+
+ //
+ // Open new handle to Dino with DELETE access
+ //
+
+ SecretHandleDinoOpen = NULL;
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &(SecretInfoDino.SecretName),
+ DELETE,
+ &SecretHandleDinoOpen
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsaDelete( SecretHandleDinoOpen );
+
+ SecretHandleDinoOpen = NULL;
+ }
+
+ return(BooleanStatus);
+}
+
+
+NTSTATUS
+CtSecretSetInfo(
+ IN PUCHAR SecretNameText,
+ IN PUCHAR CurrentValueText,
+ IN PUCHAR OldValueText,
+ OUT PCT_SECRET_INFO SecretInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets up the Secret information for a new Secret
+ prior to creation.
+
+Arguments:
+
+ SecretNameText - Pointer ASCIIZ Secret Name.
+
+ CurrentValueText - Pointer to ASCIIZ Current Value
+
+ OldValueText - Pointer to ASCIIZ Old Value
+
+ SecretInformation - Pointer to structure that will be set to
+ contain above secret information in form needed by LsaSetSecret()
+ or returned by LsaQuerySecret.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ ANSI_STRING AnsiString;
+
+ //
+ // Convert the Secret name to Unicode
+ //
+
+ RtlInitString(&AnsiString, SecretNameText);
+
+ Status = RtlAnsiStringToUnicodeString(
+ &(SecretInformation->SecretName),
+ &AnsiString,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ //
+ // Convert the Current Value to Unicode
+ //
+
+ RtlInitString(&AnsiString, CurrentValueText);
+
+ Status = RtlAnsiStringToUnicodeString(
+ &(SecretInformation->CurrentValue),
+ &AnsiString,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ //
+ // Convert the Old value to Unicode
+ //
+
+ RtlInitString(&AnsiString, OldValueText);
+
+ Status = RtlAnsiStringToUnicodeString(
+ &(SecretInformation->OldValue),
+ &AnsiString,
+ TRUE
+ );
+
+ return(Status);
+}
+
+
+BOOLEAN
+CtLsaGeneralAPI(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the General Policy Database API
+
+Parameters:
+
+ WorkstationName - Specifies the name of the target workstation. If
+ NULL or a NULL string is specified, the workstation is the local
+ machine.
+
+Return Values:
+
+ BOOLEAN - True if test successful, else FALSE
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ DbgPrint("********************************************************\n");
+ DbgPrint("LSA RPC CT - Test Lsa Policy General API \n");
+ DbgPrint("********************************************************\n");
+
+ //
+ // Test Set and Query Security object
+ //
+
+ if (!CtLsaGeneralSetQuerySecurityObject( WorkstationName )) {
+
+ goto GeneralAPIError;
+ }
+
+ //
+ // Test Privilege Enumeration
+ //
+
+ if (!CtLsaGeneralEnumeratePrivileges( WorkstationName )) {
+
+ goto GeneralAPIError;
+ }
+
+GeneralAPIFinish:
+
+ return( BooleanStatus );
+
+GeneralAPIError:
+
+ BooleanStatus = FALSE;
+ goto GeneralAPIFinish;
+}
+
+
+
+BOOLEAN
+CtLsaLookupSids(
+ IN PUNICODE_STRING WorkstationName
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaLookupSids API.
+
+Parameters:
+
+ None.
+
+Return Values:
+
+ BOOLEAN - True if test successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG Count;
+ PLSA_TRANSLATED_NAME Names = NULL;
+ PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains = NULL;
+ LSAP_WELL_KNOWN_SID_INDEX SidIndex;
+ PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSidEntry = NULL;
+ PLSA_TRUST_INFORMATION ReturnedTrustInformation = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ OBJECT_ATTRIBUTES SamObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandle = NULL;
+ PCT_UNKNOWN_SID_ENTRY UnknownSidEntry = NULL;
+ PSID Sids[LsapDummyLastSidIndex];
+ CT_UNKNOWN_SID_ENTRY UnknownSids[4];
+ UNICODE_STRING BuiltInDomainName;
+ PSID BuiltInDomainSid = NULL;
+ UNICODE_STRING AccountDomainName;
+ PSID AccountDomainSid = NULL;
+ PSID *AccountDomainGroupSids = NULL;
+ UNICODE_STRING PDAccountDomainName;
+ PSID PDAccountDomainSid = NULL;
+ PSID *PDAccountDomainGroupSids = NULL;
+ LSA_HANDLE PDPolicyHandle = NULL;
+ PSAM_RID_ENUMERATION AliasEnumerationBuffer = NULL;
+ PSAM_RID_ENUMERATION GroupEnumerationBuffer = NULL;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo;
+ PSID TrustedDomainSid = NULL;
+ LSA_HANDLE TrustedDomainHandle = NULL;
+ LSA_HANDLE PDTrustedDomainHandle = NULL;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyPDAccountDomainInfo;
+ UNICODE_STRING PDControllerName;
+
+ DbgPrint("[1] - Test General Sid Lookup\n");
+
+ //
+ // Open a handle to the target Workstation's Policy Object so that we can obtain
+ // information from it and also so that we can use it for looking up.
+ // Sids
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ WorkstationName,
+ &ObjectAttributes,
+ POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sids Test failed\n"
+ "LsaOpenPolicy failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Variation (1) - Lookup all of the Well Known Sids
+ //
+
+ DbgPrint("... [1] - Lookup all of the Well Known Sids\n");
+
+ // Using the table of Well Known Sids, construct an array containing all
+ // of the Well Known Sids in the format needed by LsaLookupSids() and
+ // look them all up.
+ //
+
+ for (SidIndex = LsapNullSidIndex;
+ SidIndex < LsapDummyLastSidIndex;
+ SidIndex++) {
+
+ //
+ // Copy the next Well Known Sid from the table of Well Known Sids.
+ //
+
+ Sids[SidIndex] = WellKnownSids[SidIndex].Sid;
+ }
+
+ Count = (ULONG) LsapDummyLastSidIndex - LsapWorldSidIndex;
+
+ ReferencedDomains = NULL;
+ Names = NULL;
+
+ Status = LsaLookupSids(
+ PolicyHandle,
+ Count,
+ &Sids[LsapWorldSidIndex],
+ &ReferencedDomains,
+ &Names
+ );
+
+ if (Status != STATUS_SUCCESS) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup all Well Known Sids failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Verify that a names array has been returned.
+ //
+
+ if (Names == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Sids\n"
+ "... no Names array returned\n"
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Now compare the information returned with that contained in the
+ // table of Well Known Sids. We will omit name checking for
+ // those Well Know Known Sids which have configurable names.
+ //
+
+ for (SidIndex = 0,
+ WellKnownSidEntry = &WellKnownSids[LsapWorldSidIndex];
+ SidIndex < (LsapDummyLastSidIndex - LsapWorldSidIndex);
+ SidIndex++, WellKnownSidEntry++) {
+
+
+ try {
+
+ //
+ // Verify that the Sid Name Use is correct.
+ //
+
+ if (Names[SidIndex].Use != WellKnownSidEntry->Use) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Sid %d mismatch on Use\n"
+ "... expected Use code %d, got %d\n",
+ SidIndex,
+ WellKnownSidEntry->Use,
+ Names[SidIndex].Use
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Verify that the translation of the Sid to a Name is correct
+ // for those non-Domain Sids that have Well Known Names. Skip
+ // this check for those with configurable names. Domain Sids
+ // are checked later.
+ //
+
+ if (WellKnownSidEntry->Use != SidTypeDomain) {
+
+ //
+ // If the Name field in the Well Known Sid Table entry
+ // has a 0 length (excepting Domain Sids), the Name is
+ // configurable and we should skip this check.
+ //
+
+ if (WellKnownSidEntry->Name.Length != 0) {
+
+ if (!RtlEqualUnicodeString(
+ &Names[SidIndex].Name,
+ &WellKnownSidEntry->Name,
+ FALSE)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Sid %d mismatch on name\n",
+ SidIndex
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+ }
+
+ //
+ // Verify that the Referenced Domain Index is as expected.
+ // This should be non-negative for all identified Sids
+ // because we return descriptive information in the
+ // Trust Information in place of a Domain Name for well
+ // well known Sids that are not Domain Sids.
+ //
+
+ if (Names[SidIndex].DomainIndex < 0) {
+
+ DbgPrint("LSA RPC CT - Lookup Well Known Sids\n");
+ DbgPrint(".. negative Domain Index returned");
+ DbgPrint(" for Sid %d\n", SidIndex);
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // The DomainIndex is non negative. Verify that a Referenced
+ // Domain List has been returned. Then check that the
+ // Referenced Entry contains the correct Trust Information.
+ //
+
+ if (ReferencedDomains == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup all well known Sids failed\n"
+ "... Referenced Domain List NULL but Well Known\n"
+ ".. Sid %d specifies a non negative index\n",
+ SidIndex
+ );
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // A Referenced Domain List was returned. Check out the
+ // Trust Information.
+ //
+
+ ReturnedTrustInformation =
+ (ReferencedDomains->Domains) + (Names[SidIndex].DomainIndex);
+
+ BooleanStatus = CtLsaGeneralVerifyTrustInfo(
+ ReturnedTrustInformation,
+ WellKnownSidEntry
+ );
+ }
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ Status = GetExceptionCode();
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Sids failed\n"
+ "Access violation accessing returned\n"
+ ".. information from LsaLookupSids\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+ }
+
+ if (!BooleanStatus) {
+
+ DbgPrint("LSA RPC CT - Lookup Well Known Sids failed\n");
+ }
+
+ //
+ // Variation (2) - Lookup the Alias Sids in the Built-In Sam Domain
+ //
+
+ DbgPrint(
+ "... [2] - Lookup Alias Account Sids present in Workstation's\n"
+ "..........Built-in SAM Domain\n"
+ );
+
+ CtLsaInitObjectAttributes(
+ &SamObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+
+ RtlInitUnicodeString( &BuiltInDomainName, L"BUILTIN" );
+
+ if (!CtLsaLookupSidsInSamDomain(
+ WorkstationName,
+ WorkstationName,
+ &BuiltInDomainName,
+ CT_SAM_ALIAS
+ )) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Variation [3] - Lookup the Group Sids contained in the Workstation's
+ // SAM Account Domain
+ //
+ // First, obtain the Name and Sid of the SAM Account Domain from the
+ // Workstation's LSA Policy Object.
+ //
+
+ DbgPrint(
+ "... [3] - Lookup User and Group Account Sids present in Workstation's\n"
+ "..........SAM Account Domain\n"
+ );
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Workstation SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Account Domain Name/Sid returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ AccountDomainName = PolicyAccountDomainInfo->DomainName;
+
+ CtLsaInitObjectAttributes(
+ &SamObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ if (!CtLsaLookupSidsInSamDomain(
+ WorkstationName,
+ WorkstationName,
+ &AccountDomainName,
+ CT_SAM_USER
+ )) {
+
+ goto LookupSidsError;
+ }
+
+ if (!CtLsaLookupSidsInSamDomain(
+ WorkstationName,
+ WorkstationName,
+ &AccountDomainName,
+ CT_SAM_GROUP
+ )) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Variation [4] - Lookup the Group Sids contained in the Primary Domain's
+ // SAM Account Domain
+ //
+ // We need to obtain the Name of a Primary Domain Controller so that
+ // we can connect to the LSA Policy Object and SAM Servers there.
+ // To get this takes several steps -
+ //
+ // * Query the Workstation Policy object to get the Sid of the Primary
+ // Domain.
+ //
+ // * Use the primary Domain Sid to open the Trusted Domain object
+ // for the Primary Domain.
+ //
+ // * Query the Trusted Controller List from the TD object.
+ //
+ // * Select the name of the first (or any) Controller on the list
+ // to open the LSA and SAM objects.
+ //
+ // Obtain the Sid of the Workstation's Primary Domain from the
+ // Policy Object.
+ //
+
+ DbgPrint(
+ "... [4] - Lookup User and Group Account Sids present in Workstation's\n"
+ "..........Primary Domain SAM Account Domain\n"
+ );
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Primary Domain Name/Sid returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // If the Primary Domain Sid is NULL, return an error.
+ //
+
+ if (PolicyPrimaryDomainInfo->Sid == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Primary Domain Name/Sid returned NULL Sid\n"
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Now open the Trusted Domain object corresponding to the
+ // Primary Domain Sid.
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ PolicyPrimaryDomainInfo->Sid,
+ TRUSTED_QUERY_CONTROLLERS,
+ &TrustedDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaOpenTrustedDomain for Primary Domain Trusted Object returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Now query the Primary Domain's Controller List
+ //
+
+ Status = LsaQueryInfoTrustedDomain(
+ TrustedDomainHandle,
+ TrustedControllersInformation,
+ (PVOID *) &TrustedControllersInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInfoTrustedDomain for Primary Domain Ctrlrs 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // If no Trusted Controllers were returned, return an error.
+ //
+
+ if (TrustedControllersInfo->Entries == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Primary Domain SAM Account Domain failed\n"
+ "... no PD Controllers returned\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Now open a handle to the Lsa Policy Object for the first
+ // controller on the list.
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PDPolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ &TrustedControllersInfo->Names[0],
+ &ObjectAttributes,
+ POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
+ &PDPolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaOpenPolicy for PD LSA returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Obtain the Name and Sid of the SAM Account Domain from the
+ // Primary Domain's LSA Policy Object.
+ //
+
+ Status = LsaQueryInformationPolicy(
+ PDPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) &PolicyPDAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Account Domain Name/Sid returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Now Lookup the Group and User accounts in the SAM Account object
+ // on this Domain Controller for the Primary Domain.
+ //
+
+ PDAccountDomainName = PolicyPDAccountDomainInfo->DomainName;
+ PDControllerName = TrustedControllersInfo->Names[0];
+
+ if (!CtLsaLookupSidsInSamDomain(
+ WorkstationName,
+ &PDControllerName,
+ &PDAccountDomainName,
+ CT_SAM_USER
+ )) {
+
+ goto LookupSidsError;
+ }
+
+
+ if (!CtLsaLookupSidsInSamDomain(
+ WorkstationName,
+ &PDControllerName,
+ &PDAccountDomainName,
+ CT_SAM_GROUP
+ )) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Variation (5) - Lookup Group and User account Sids in a Domain
+ // controller for a Domain that is trusted by the workstation's
+ // Primary Domain.
+ //
+
+ DbgPrint(
+ "... [5] - Lookup User and Group Account Sids present in Workstation's\n"
+ "..........Primary Domain Trusted Domain SAM Account Domain\n"
+ );
+
+ DbgPrint("..... Test TBS\n");
+
+ //
+ // Variation (6) - Lookup some completely unknown Sids.
+ //
+
+ DbgPrint("... [5] - Lookup Unknown Sids\n");
+
+ BooleanStatus = CtLsaGeneralInitUnknownSid(
+ FredSid,
+ &UnknownSids[0],
+ FALSE
+ );
+
+ if (!BooleanStatus) {
+
+ goto LookupSidsError;
+ }
+
+ BooleanStatus = CtLsaGeneralInitUnknownSid(
+ WilmaSid,
+ &UnknownSids[1],
+ FALSE
+ );
+
+ if (!BooleanStatus) {
+
+ goto LookupSidsError;
+ }
+
+ BooleanStatus = CtLsaGeneralInitUnknownSid(
+ PebblesSid,
+ &UnknownSids[2],
+ FALSE
+ );
+
+ if (!BooleanStatus) {
+
+ goto LookupSidsError;
+ }
+
+ BooleanStatus = CtLsaGeneralInitUnknownSid(
+ DinoSid,
+ &UnknownSids[3],
+ FALSE
+ );
+
+ if (!BooleanStatus) {
+
+ goto LookupSidsError;
+ }
+
+ ReferencedDomains = NULL;
+ Names = NULL;
+
+ Count = 4;
+
+ //
+ // Construct an array containing the unknown Sids.
+ //
+
+ for (SidIndex = 0;
+ SidIndex < 4;
+ SidIndex++) {
+
+ //
+ // Copy the next Unknown Sid
+ //
+
+ Sids[SidIndex] = UnknownSids[SidIndex].Sid;
+ }
+
+ Status = LsaLookupSids(
+ PolicyHandle,
+ Count,
+ Sids,
+ &ReferencedDomains,
+ &Names
+ );
+
+ if (Status != STATUS_SUCCESS) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Sids failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Now compare the information returned with that contained in the
+ // table of Unknown Sids.
+ //
+
+ for (SidIndex = 0, UnknownSidEntry = UnknownSids;
+ SidIndex < 4;
+ SidIndex++, UnknownSidEntry++) {
+
+ try {
+
+ //
+ // Verify that the Sid Name Use is correct. We should
+ // have SidTypeUnknown in all cases.
+ //
+
+ if (Names[SidIndex].Use != SidTypeUnknown) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Sids failed\n"
+ "... Unknown Sid %d mismatch on Use\n",
+ "... expected Use code %d (SidTypeUnknown), got %d\n",
+ SidIndex,
+ SidTypeUnknown,
+ Names[SidIndex].Use
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Verify that the translation of the Sid to a Name is correct.
+ // For an Unknown Sid, the translation is a Unicode representation
+ // of the Relative Id (if the Sid's Domain is known) or the
+ // full Sid (if the Sid's Domain is unknown.
+ //
+
+ if (!RtlEqualUnicodeString(
+ &Names[SidIndex].Name,
+ &UnknownSidEntry->Name,
+ FALSE)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Sids failed\n"
+ "... Unknown Sid %d mismatch on name\n",
+ SidIndex
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Verify that the Referenced Domain Index is as expected.
+ // This should be non-negative for all identified Sids
+ // because we return descriptive information in the
+ // Trust Information in place of a Domain Name for well
+ // well known Sids that are not Domain Sids.
+ //
+
+ if (!UnknownSids[SidIndex].DomainKnown) {
+
+ if (Names[SidIndex].DomainIndex >= 0) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Sids failed\n"
+ "... Incorrect DomainIndex returned\n"
+ ".. for Sid %d\n"
+ ".. non-neg value returned when domain unknown\n",
+ SidIndex
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Domain is expected to be known.
+ //
+
+ if (Names[SidIndex].DomainIndex < 0) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Sids failed\n"
+ "... Incorrect DomainIndex returned\n"
+ "... for Sid %d\n"
+ "... Negative value returned when domain known\n",
+ SidIndex
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // The DomainIndex is non negative. Verify that a Referenced
+ // Domain List has been returned. Then check that the
+ // Referenced Entry contains the correct Trust Information.
+ //
+
+ if (BooleanStatus) {
+
+ if (ReferencedDomains == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Sids failed\n"
+ ".. Referenced Domains List NULL\n"
+ );
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // A Referenced Domain List was returned. Check out the
+ // Trust Information.
+ //
+
+ ReturnedTrustInformation =
+ (ReferencedDomains->Domains) + (Names[SidIndex].DomainIndex);
+
+ BooleanStatus = CtLsaGeneralVerifyTrustInfo(
+ ReturnedTrustInformation,
+ WellKnownSidEntry
+ );
+ }
+ }
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ Status = GetExceptionCode();
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Sids failed\n"
+ "... Access violation accessing returned\n"
+ "... information from lookup of all well known Sids\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ DbgPrint(".. no further Sid output compared\n");
+ break;
+ }
+ }
+
+
+LookupSidsFinish:
+
+ //
+ // If necessary, free the ReferencedDomains array.
+ //
+
+ if (ReferencedDomains != NULL) {
+
+ Status = LsaFreeMemory(ReferencedDomains);
+ ReferencedDomains = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sids Lookup failed\n"
+ "... LsaFreeMemory(ReferencedDomains) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If necessary, free the Names array.
+ //
+
+ if (Names != NULL) {
+
+ Status = LsaFreeMemory(Names);
+ Names = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sids Lookup failed\n"
+ "... LsaFreeMemory(Names) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If necessary, close the TrustedDomainHandle.
+ //
+
+ if (TrustedDomainHandle != NULL) {
+
+ Status = LsaClose( TrustedDomainHandle );
+
+ TrustedDomainHandle = NULL;
+ }
+
+ //
+ // If necessary, close the Policy handle to the LSA object on the
+ // PDC.
+ //
+
+ if (PDPolicyHandle != NULL) {
+
+ Status = LsaClose(PDPolicyHandle);
+ PDPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sids Lookup failed\n"
+ "... LsaClose(PDPolicyHandle) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If necessary, close the LSA Policy Handle for the Workstation.
+ //
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose(PolicyHandle);
+ PolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sids Lookup failed\n"
+ "... LsaClose(PolicyHandle) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ return(BooleanStatus);
+
+LookupSidsError:
+
+ goto LookupSidsFinish;
+}
+
+
+BOOLEAN
+CtLsaLookupSidsInSamDomain(
+ IN OPTIONAL PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING DomainControllerName,
+ IN PUNICODE_STRING SamDomainName,
+ IN CT_SAM_ACCOUNT_TYPE SamAccountType
+ )
+
+/*++
+
+Routine Description:
+
+ This function enumerates all the SAM accounts of a specified type
+ in a specified SAM domain on a specified target system. The system
+ must be one of the following:
+
+ o The Workstation itself.
+ o A Domain Controller for the Primary Domain of the Workstation.
+ o A Domain Controller for one of the Trusted Domains of the
+ Workstation.
+
+
+ Having enumerated the accounts, the function then performs
+ an LsaLookupSids call via the specified Workstation to lookup all of
+ these account Sids, and then compares the returned information
+ with that expected.
+
+Arguments:
+
+ WorkstationName - Specifies a Workstation Name. The name may be
+ the NULL string, which means the current system.
+
+ DomainControllerName - Specifies the name of a target Domain Controller
+ for (the Workstation's Primary Domain or one of its Trusted
+ Domains.
+
+ SamDomainName - Specifies the name of the SAM Domain. This is either
+ the BUILTIN Domain or the name of the Accounts Domain.
+
+ SamAccountType - Specifies the type of SAM account to be enumerated
+ and looked up.
+
+Return Values:
+
+ BOOLEAN - TRUE if successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ SAM_HANDLE SamServerHandle = NULL;
+ SAM_HANDLE SamDomainHandle = NULL;
+ OBJECT_ATTRIBUTES SamObjectAttributes;
+ OBJECT_ATTRIBUTES LsaObjectAttributes;
+ PSID SamDomainSid = NULL;
+ PSAM_RID_ENUMERATION EnumerationBuffer = NULL;
+ PSID *AccountSids = NULL;
+ ULONG AccountSidsLength;
+ PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains = NULL;
+ PLSA_TRANSLATED_NAME Names = NULL;
+ ULONG RidIndex;
+ ULONG UserAccountControl;
+ LSA_HANDLE PolicyHandle = NULL;
+
+ UNICODE_STRING TmpDomainControllerName;
+
+ //
+ // Connect to the SAM server.
+ //
+
+ Status = SamConnect(
+ DomainControllerNameCopy,
+ &SamServerHandle,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ &SamObjectAttributes
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamConnect returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Lookup the Named Domain in the Sam Server to get its Sid.
+ //
+
+ Status = SamLookupDomainInSamServer(
+ SamServerHandle,
+ SamDomainName,
+ &SamDomainSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamLookupDomainInSamServer returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Open the Domain
+ //
+
+ Status = SamOpenDomain(
+ SamServerHandle,
+ DOMAIN_LIST_ACCOUNTS,
+ SamDomainSid,
+ &SamDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamOpenDomain returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Enumerate the accounts of the specified type in the specified SAM Domain
+ //
+
+ CountReturned = 0;
+ EnumerationContext = 0;
+ EnumerationBuffer = NULL;
+ PreferedMaximumLength = 512;
+
+ switch (SamAccountType) {
+
+ case CT_SAM_ALIAS:
+
+ Status = SamEnumerateAliasesInDomain(
+ SamDomainHandle,
+ &EnumerationContext,
+ (PVOID *) &EnumerationBuffer,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamEnumerateAliasInDomain returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case CT_SAM_GROUP:
+
+ Status = SamEnumerateGroupsInDomain(
+ SamDomainHandle,
+ &EnumerationContext,
+ (PVOID *) &EnumerationBuffer,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamEnumerateGroupsInDomain returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case CT_SAM_USER:
+
+ UserAccountControl = 0;
+
+ Status = SamEnumerateUsersInDomain(
+ SamDomainHandle,
+ &EnumerationContext,
+ UserAccountControl,
+ (PVOID *) &EnumerationBuffer,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamEnumerateUsersInDomain returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ }
+
+ if (!BooleanStatus) {
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ if (CountReturned == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamEnumerate<AccountType>Domain returned no Ids\n"
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Now construct the Account Sids from the Rids just enumerated.
+ // We prepend the Sam Domain Sid to the Rids.
+ //
+
+ AccountSidsLength = CountReturned * sizeof ( PSID );
+
+ AccountSids = (PSID *) LocalAlloc( LMEM_FIXED, AccountSidsLength );
+
+ if (AccountSids == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... Unable to allocate memory for Built In Domain Alias Sids\n"
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ RtlZeroMemory( AccountSids, AccountSidsLength );
+
+
+ for (RidIndex = 0; RidIndex < CountReturned; RidIndex++) {
+
+ if (!CtLsaGeneralBuildSid(
+ &(AccountSids[ RidIndex ]),
+ SamDomainSid,
+ EnumerationBuffer[ RidIndex ].RelativeId
+ )) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... not enough memory to build Sid from DomainSid and Rid\n"
+ );
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+
+ if (!BooleanStatus) {
+
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Now Lookup these Account Sids via LsaLookupSids specifying the
+ // Workstation. First, we need to open a handle to the workstation's
+ // Policy object.
+ //
+
+ CtLsaInitObjectAttributes(
+ &LsaObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ WorkstationName,
+ &LsaObjectAttributes,
+ POLICY_LOOKUP_NAMES,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sids in Sam Domain failed\n"
+ "LsaOpenPolicy for Workstation returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ ReferencedDomains = NULL;
+ Names = NULL;
+
+ Status = LsaLookupSids(
+ PolicyHandle,
+ CountReturned,
+ AccountSids,
+ &ReferencedDomains,
+ &Names
+ );
+
+ if (Status != STATUS_SUCCESS) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... LsaLookupSids returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Verify that a ReferencedDomains structure has been returned.
+ //
+
+ if (ReferencedDomains == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... no ReferencedDomains structure returned\n");
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Verify that a Names array has been returned.
+ //
+
+ if (Names == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... no Names array returned\n"
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+
+ //
+ // Finally, compare the Names returned from SAM directly with the
+ // names returned from LsaLookupSids.
+ //
+
+ for ( RidIndex = 0; RidIndex < CountReturned; RidIndex++ ) {
+
+ if (!RtlEqualUnicodeString(
+ &EnumerationBuffer[ RidIndex].Name,
+ &Names[ RidIndex ].Name,
+ TRUE
+ )) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... mismatch on name for Sid %d\n",
+ RidIndex
+ );
+
+ DbgPrint("... no further names compared\n");
+ goto LookupSidsInSamDomainError;
+ }
+ }
+
+LookupSidsInSamDomainFinish:
+
+ //
+ // If necessary, close the SAM Domain Handle for the Workstation.
+ //
+
+ if (SamDomainHandle != NULL) {
+
+ Status = SamCloseHandle( SamDomainHandle);
+
+ SamDomainHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamCloseHandle ( Sam Domain Handle ) returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+ }
+
+ //
+ // If necessary, disconnect from the SAM Server.
+ //
+
+ if (SamServerHandle != NULL) {
+
+ Status = SamCloseHandle( SamServerHandle );
+
+ SamServerHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... SamCloseHandle( Sam Server Handle ) returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+ }
+
+ //
+ // If necessary, close the LSA handle to the Workstation's Policy object.
+ //
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose( PolicyHandle );
+
+ PolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Sid Lookup in SAM Domain failed\n"
+ "... LsaClose( Policy Server Handle ) returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupSidsInSamDomainError;
+ }
+ }
+
+ return( BooleanStatus );
+
+LookupSidsInSamDomainError:
+
+ BooleanStatus = FALSE;
+
+ goto LookupSidsInSamDomainFinish;
+}
+
+
+BOOLEAN
+CtLsaLookupNames(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaLookupNames API.
+
+Parameters:
+
+ WorkstationName - Specifies the name of the Workstation. If NULL or
+ a NULL string is specified, the workstation is the local machine.
+
+Return Values:
+
+ BOOLEAN - True if test successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG Count;
+ PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains;
+ LSAP_WELL_KNOWN_SID_INDEX NameIndex;
+ PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSidEntry;
+ PLSA_TRUST_INFORMATION ReturnedTrustInformation;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandle = NULL;
+ LSA_HANDLE PDPolicyHandle = NULL;
+ LSA_HANDLE TrustedDomainHandle = NULL;
+ ULONG ExpectedRid;
+ PLSA_TRANSLATED_SID Sids = NULL;
+ CT_UNKNOWN_NAME_ENTRY UnknownNameInfo[CT_UNKNOWN_NAME_COUNT];
+ UCHAR SubAuthorityCount;
+ UNICODE_STRING BuiltInDomainName;
+ UNICODE_STRING AccountDomainName;
+ UNICODE_STRING PDAccountDomainName;
+ UNICODE_STRING InputNames[LsapDummyLastSidIndex];
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo = NULL;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyPDAccountDomainInfo = NULL;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
+ UNICODE_STRING UnknownNames[CT_UNKNOWN_NAME_COUNT];
+ PCT_UNKNOWN_NAME_ENTRY UnknownNameEntry = NULL;
+
+ DbgPrint("[2] - Test General Name Lookup\n");
+
+ //
+ // Open a handle to the Policy Object
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ WorkstationName,
+ &ObjectAttributes,
+ POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Policy object open handle %d failed 0x%lx\n",
+ Index,
+ Status
+ );
+ return FALSE;
+ }
+
+ //
+ // Variation [1] - Lookup all of the Well Known Names
+ //
+
+
+ DbgPrint("... [1] - Lookup all of the Well Known Names\n");
+
+ // Using the table of Well Known Sids/Names, construct an array containing all
+ // of the Well Known Names in the format needed by LsaLookupNames() and
+ // look them all up.
+ //
+
+ for (NameIndex = LsapNullSidIndex;
+ NameIndex < LsapDummyLastSidIndex;
+ NameIndex++) {
+
+ //
+ // Copy the next Well Known Name from the table of Well Known Names.
+ //
+
+ InputNames[NameIndex] = WellKnownSids[NameIndex].Name;
+ }
+
+ Count = (ULONG) LsapDummyLastSidIndex - LsapWorldSidIndex;
+
+ ReferencedDomains = NULL;
+ Sids = NULL;
+
+ Status = LsaLookupNames(
+ PolicyHandle,
+ Count,
+ &InputNames[LsapWorldSidIndex],
+ &ReferencedDomains,
+ &Sids
+ );
+
+ //
+ // Not all of the well known Sids have Well Known Names, so we should
+ // get STATUS_SOME_NOT_MAPPED back.
+
+ if (Status != STATUS_SOME_NOT_MAPPED) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup all Well Known Names returned 0x%lx\n",
+ "... expected 0x%lx (STATUS_SOME_NOT_MAPPED)\n",
+ "[ The unmapped Names are NULL names, one name for\n"
+ " each Well Known Sid that does not have a Well Known Name ]\n",
+ Status,
+ STATUS_SOME_NOT_MAPPED
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Verify that a Sids array has been returned.
+ //
+
+ if (Sids == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Names\n"
+ "... no Sids array returned\n"
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Now compare the information returned with that contained in the
+ // table of Well Known Names.
+ //
+
+ for (NameIndex = 0,
+ WellKnownSidEntry = &WellKnownSids[LsapWorldSidIndex];
+ NameIndex < (LsapDummyLastSidIndex - LsapWorldSidIndex);
+ NameIndex++, WellKnownSidEntry++) {
+
+
+ try {
+
+ //
+ // If this entry in the Well Known Sids table has a Well Known
+ // Name, check the output from LsaLookupNames.
+ //
+
+ if (WellKnownSidEntry->Name.Length != 0) {
+
+
+ //
+ // Verify that the Sid Name Use is correct.
+ //
+
+ if (Sids[NameIndex].Use != WellKnownSidEntry->Use) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Name %d mismatch on Use\n"
+ "... expected Use code %d, got %d\n",
+ NameIndex,
+ WellKnownSidEntry->Use,
+ Sids[NameIndex].Use
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Verify that the Referenced Domain Index is as expected.
+ // and validate the Trust Information. The Referenced Domain
+ // index should be non-negative for all identified Names
+ // because we return descriptive information in the
+ // Trust Information in place of a Domain Name for well
+ // well known Names that are not Domain Names.
+ //
+
+ if (Sids[NameIndex].DomainIndex < 0) {
+
+ DbgPrint("LSA RPC CT - Lookup Well Known Names\n");
+ DbgPrint(".. negative Domain Index returned");
+ DbgPrint(" for Name %d\n", NameIndex);
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // The DomainIndex is non negative. Verify that a Referenced
+ // Domain List has been returned. Then check that the
+ // Referenced Entry contains the correct Trust Information.
+ //
+
+ if (ReferencedDomains == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup all well known Names failed\n"
+ "... Referenced Domain List NULL but Well Known\n"
+ ".. Name %d specifies a non negative index\n",
+ NameIndex
+ );
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // A Referenced Domain List was returned. Check out the
+ // Trust Information.
+ //
+
+ ReturnedTrustInformation =
+ (ReferencedDomains->Domains) + (Sids[NameIndex].DomainIndex);
+
+ BooleanStatus = CtLsaGeneralVerifyTrustInfo(
+ ReturnedTrustInformation,
+ WellKnownSidEntry
+ );
+ }
+ }
+
+ } else {
+
+ //
+ // This entry has no Well Known Name. We should get
+ // SidTypeUnknown back in the Translated Sids info.
+ //
+
+ if (Sids[NameIndex].Use != SidTypeUnknown) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Names failed\n"
+ "Use SidTypeUnknown expected for NULL Name %d\n",
+ NameIndex
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ Status = GetExceptionCode();
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Well Known Names failed\n"
+ "Access violation accessing returned\n"
+ ".. information from LsaLookupNames\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ DbgPrint("... no further Name output compared\n");
+ break;
+ }
+
+ //
+ // If the Sid is not the Sid of a Domain and the Sid has a well
+ // known name, verify that the RelativeId returned matches that
+ // expected. First, compute the Relative Id expected from the Well
+ // Known Sid Entry.
+ //
+
+ if (WellKnownSidEntry->Use != SidTypeDomain) {
+
+ if (WellKnownSidEntry->Name.Length != 0) {
+
+ SubAuthorityCount = *RtlSubAuthorityCountSid( WellKnownSidEntry-> Sid);
+
+ ExpectedRid = *RtlSubAuthoritySid(
+ WellKnownSidEntry->Sid,
+ SubAuthorityCount - 1
+ );
+
+ if (Sids[NameIndex].RelativeId != ExpectedRid) {
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+
+ } else {
+
+ //
+ // The Sid is the Sid of a Domain. Verify that the correct
+ // Trust Information has been returned.
+ //
+
+ // TBS
+ }
+ }
+
+ if (!BooleanStatus) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Variation [2] - Lookup the Alias Names in the Built-In Sam Domain
+ //
+
+ DbgPrint(
+ "... [2] - Lookup Alias Account Names present in Workstation's\n"
+ "..........Built-in SAM Domain\n"
+ );
+
+ RtlInitUnicodeString( &BuiltInDomainName, L"BUILTIN" );
+
+ //
+ // Enumerate the Aliases in the Built-in Sam Domain, convert them to
+ // names and then Lookup those Names via LsaLookupNames.
+ //
+
+
+ if (!CtLsaLookupNamesInSamDomain(
+ WorkstationName,
+ WorkstationName,
+ &BuiltInDomainName,
+ CT_SAM_ALIAS
+ )) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Variation [3] - Lookup the User and Group Names contained in the Workstation's
+ // SAM Account Domain
+ //
+ // First, obtain the Name and Name of the SAM Account Domain from the
+ // Workstation's LSA Policy Object.
+ //
+
+ DbgPrint(
+ "... [3] - Lookup User and Group Account Names present in Workstation's\n"
+ "..........SAM Account Domain\n"
+ );
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Workstation SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Account Domain Name/Name returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ AccountDomainName = PolicyAccountDomainInfo->DomainName;
+
+ //
+ // Enumerate the Users in the Account Domain, convert them to
+ // names and then Lookup those Names via LsaLookupNames.
+ //
+
+ if (!CtLsaLookupNamesInSamDomain(
+ WorkstationName,
+ WorkstationName,
+ &AccountDomainName,
+ CT_SAM_USER
+ )) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Enumerate the Groups in the Account Domain, convert them to
+ // names and then Lookup those Names via LsaLookupNames.
+ //
+
+ if (!CtLsaLookupNamesInSamDomain(
+ WorkstationName,
+ WorkstationName,
+ &AccountDomainName,
+ CT_SAM_GROUP
+ )) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Variation [4] - Lookup the User and Group Names contained in the Primary Domain's
+ // SAM Account Domain
+ //
+
+ DbgPrint(
+ "... [4] - Lookup User and Group Account Names present in Workstation's\n"
+ "..........Primary Domain's SAM Account Domain\n"
+ );
+
+ //
+ // We need to obtain the Name of a Primary Domain Controller so that
+ // we can connect to the LSA Policy Object and SAM Servers there.
+ // To get this takes several steps -
+ //
+ // * Query the Workstation Policy object to get the Name of the Primary
+ // Domain.
+ //
+ // * Use the primary Domain Name to open the Trusted Domain object
+ // for the Primary Domain.
+ //
+ // * Query the Trusted Controller List from the TD object.
+ //
+ // * Select the name of the first (or any) Controller on the list
+ // to open the LSA and SAM objects.
+ //
+ // Obtain the Name of the Workstation's Primary Domain from the
+ // Policy Object.
+ //
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Primary Domain Name/Name returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // If the Primary Domain Name is NULL, return an error.
+ //
+
+ if (PolicyPrimaryDomainInfo->Name.Buffer == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Primary Domain Name/Name returned NULL Name\n"
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Now open the Trusted Domain object corresponding to the
+ // Primary Domain Name.
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ PolicyPrimaryDomainInfo->Sid,
+ TRUSTED_QUERY_CONTROLLERS,
+ &TrustedDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaOpenTrustedDomain for Primary Domain Trusted Object returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Now query the Primary Domain's Controller List
+ //
+
+ Status = LsaQueryInfoTrustedDomain(
+ TrustedDomainHandle,
+ TrustedControllersInformation,
+ (PVOID *) &TrustedControllersInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInfoTrustedDomain for Primary Domain Ctrlrs 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // If no Trusted Controllers were returned, return an error.
+ //
+
+ if (TrustedControllersInfo->Entries == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Primary Domain SAM Account Domain failed\n"
+ "... no PD Controllers returned\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Now open a handle to the Lsa Policy Object for the first
+ // controller on the list.
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PDPolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ &TrustedControllersInfo->Names[0],
+ &ObjectAttributes,
+ POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
+ &PDPolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaOpenPolicy for PD LSA returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Obtain the Name and Sid of the SAM Account Domain from the
+ // Primary Domain's LSA Policy Object.
+ //
+
+ Status = LsaQueryInformationPolicy(
+ PDPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) &PolicyPDAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in Primary Domain SAM Account Domain failed\n"
+ "... LsaQueryInformationPolicy for Account Domain Name/Name returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ PDAccountDomainName = PolicyPDAccountDomainInfo->DomainName;
+
+ //
+ // Enumerate the Users in the Account Domain located on a controller
+ // for the primary Domain, and then Lookup those Names via LsaLookupNames.
+ //
+
+ if (!CtLsaLookupNamesInSamDomain(
+ WorkstationName,
+ &TrustedControllersInfo->Names[0],
+ &PDAccountDomainName,
+ CT_SAM_USER
+ )) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Enumerate the Groups in the Account Domain, convert them to
+ // names and then Lookup those Names via LsaLookupNames.
+ //
+
+ if (!CtLsaLookupNamesInSamDomain(
+ WorkstationName,
+ &TrustedControllersInfo->Names[0],
+ &PDAccountDomainName,
+ CT_SAM_GROUP
+ )) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Variation [5] - Lookup Names of Groups and Users in the SAM Account Domain of
+ // a Controller for one of the Trusted Domains
+ //
+
+ DbgPrint(
+ "... [5] - Lookup User and Group Account Names present in Workstation's\n"
+ "..........Primary Domain Trusted Domain SAM Account Domain\n"
+ );
+
+ // TBS
+
+ //
+ // Variation (6) - Lookup some completely unknown isolated Names.
+ //
+
+ DbgPrint("... [6] - Lookup Unknown Names\n");
+
+ CtLsaGeneralInitUnknownName(
+ &FredName,
+ NULL,
+ NULL,
+ &UnknownNameInfo[0],
+ FALSE
+ );
+
+ CtLsaGeneralInitUnknownName(
+ &WilmaName,
+ NULL,
+ NULL,
+ &UnknownNameInfo[1],
+ FALSE
+ );
+
+ CtLsaGeneralInitUnknownName(
+ &PebblesName,
+ &BedrockCDomainName,
+ BedrockCDomainSid,
+ &UnknownNameInfo[2],
+ FALSE
+ );
+
+ CtLsaGeneralInitUnknownName(
+ &DinoName,
+ &BedrockDDomainName,
+ BedrockDDomainSid,
+ &UnknownNameInfo[3],
+ FALSE
+ );
+
+ ReferencedDomains = NULL;
+
+ Count = 4;
+
+ //
+ // Construct an array containing the unknown Names.
+ //
+
+ for (NameIndex = 0;
+ NameIndex < 4;
+ NameIndex++) {
+
+ //
+ // Copy the next Unknown Name
+ //
+
+ UnknownNames[NameIndex] = UnknownNameInfo[NameIndex].Name;
+ }
+
+ Status = LsaLookupNames(
+ PolicyHandle,
+ Count,
+ UnknownNames,
+ &ReferencedDomains,
+ &Sids
+ );
+
+ if (Status != STATUS_SUCCESS) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Names failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Now compare the information returned with that contained in the
+ // table of Unknown Names.
+ //
+
+ for (NameIndex = 0, UnknownNameEntry = UnknownNameInfo;
+ NameIndex < 4;
+ NameIndex++, UnknownNameEntry++) {
+
+ try {
+
+ //
+ // Verify that the Name Name Use is correct. We should
+ // have NameTypeUnknown in all cases.
+ //
+
+ if (Sids[NameIndex].Use != SidTypeUnknown) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Names failed\n"
+ "... Unknown Name %d Use incorrect\n",
+ "... expected Use code %d (SidTypeUnknown), got %d\n",
+ NameIndex,
+ SidTypeUnknown,
+ Sids[NameIndex].Use
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Verify that the Referenced Domain Index is as expected.
+ // This should be non-negative for all identified Names
+ // because we return descriptive information in the
+ // Trust Information in place of a Domain Name for well
+ // well known Names that are not Domain Names.
+ //
+
+ if (!UnknownNameInfo[NameIndex].DomainKnown) {
+
+ if (Sids[NameIndex].DomainIndex >= 0) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Names failed\n"
+ "... Incorrect DomainIndex returned\n"
+ ".. for Name %d\n"
+ ".. non-neg value returned when domain unknown\n",
+ NameIndex
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ } else {
+
+ //
+ // Domain is expected to be known.
+ //
+
+ if (Sids[NameIndex].DomainIndex < 0) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Names failed\n"
+ "... Incorrect DomainIndex returned\n"
+ "... for Name %d\n"
+ "... Negative value returned when domain known\n",
+ NameIndex
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // The DomainIndex is non negative. Verify that a Referenced
+ // Domain List has been returned. Then check that the
+ // Referenced Entry contains the correct Trust Information.
+ //
+
+ if (BooleanStatus) {
+
+ if (ReferencedDomains == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Names failed\n"
+ ".. Referenced Domains List NULL\n"
+ );
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ //
+ // A Referenced Domain List was returned. Check out the
+ // Trust Information.
+ //
+
+ ReturnedTrustInformation =
+ (ReferencedDomains->Domains) + (Sids[NameIndex].DomainIndex);
+
+ /*
+
+ NOTE - This test is TBS
+
+ BooleanStatus = CtLsaGeneralVerifyNameTrustInfo(
+ ReturnedTrustInformation,
+ UnknownNameInfo + NameIndex;
+ )
+ */
+ }
+ }
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ Status = GetExceptionCode();
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Unknown Names failed\n"
+ "... Access violation accessing returned\n"
+ "... information from lookup of all well known Names\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ DbgPrint(".. no further Sid output compared\n");
+ break;
+ }
+ }
+
+
+LookupNamesFinish:
+
+ //
+ // If necessary, free the ReferencedDomains array.
+ //
+
+ if (ReferencedDomains != NULL) {
+
+ Status = LsaFreeMemory(ReferencedDomains);
+ ReferencedDomains = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Names Lookup failed\n"
+ "... LsaFreeMemory(ReferencedDomains) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If necessary, free the Sids array.
+ //
+
+ if (Sids != NULL) {
+
+ Status = LsaFreeMemory(Sids);
+ Sids = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Names Lookup failed\n"
+ "... LsaFreeMemory(Sids) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If necessary, close the TrustedDomainHandle.
+ //
+
+ if (TrustedDomainHandle != NULL) {
+
+ Status = LsaClose( TrustedDomainHandle );
+
+ TrustedDomainHandle = NULL;
+ }
+
+ //
+ // If necessary, close the Policy handle to the LSA object on the
+ // PDC.
+ //
+
+ if (PDPolicyHandle != NULL) {
+
+ Status = LsaClose(PDPolicyHandle);
+ PDPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Names Lookup failed\n"
+ "... LsaClose(PDPolicyHandle) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // If necessary, close the LSA Policy Handle for the Workstation.
+ //
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose(PolicyHandle);
+ PolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Names Lookup failed\n"
+ "... LsaClose(PolicyHandle) returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+ }
+
+ return(BooleanStatus);
+
+LookupNamesError:
+
+ BooleanStatus = FALSE;
+
+ goto LookupNamesFinish;
+}
+
+
+BOOLEAN
+CtLsaLookupNamesInSamDomain(
+ IN OPTIONAL PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING DomainControllerName,
+ IN PUNICODE_STRING SamDomainName,
+ IN CT_SAM_ACCOUNT_TYPE SamAccountType
+ )
+
+/*++
+
+Routine Description:
+
+ This function enumerates all the SAM accounts of a specified type
+ in a specified SAM domain on a specified target system. The system
+ must be one of the following:
+
+ o The Workstation itself.
+ o A Domain Controller for the Primary Domain of the Workstation.
+ o A Domain Controller for one of the Trusted Domains of the
+ Workstation.
+
+
+ Having enumerated the accounts, the function then performs
+ an LsaLookupNames call via the specified Workstation to lookup all of
+ these account names, and then compares the returned information
+ with that expected.
+
+Arguments:
+
+ WorkstationName - Specifies a Workstation Name. The name may be
+ the NULL string, which means the current system.
+
+
+ DomainControllerName - Specifies the name of a target Domain Controller
+ for (the Workstation's Primary Domain or one of its Trusted
+ Domains.
+
+ SamDomainName - Specifies the name of the SAM Domain. This is either
+ the BUILTIN Domain or the name of the Accounts Domain.
+
+ SamAccountType - Specifies the type of SAM account to be enumerated
+ and looked up.
+
+Return Values:
+
+ BOOLEAN - TRUE if successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ SAM_HANDLE SamServerHandle = NULL;
+ SAM_HANDLE SamDomainHandle = NULL;
+ OBJECT_ATTRIBUTES SamObjectAttributes;
+ OBJECT_ATTRIBUTES LsaObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ PSID SamDomainSid = NULL;
+ ULONG CountReturned;
+ ULONG EnumerationContext;
+ PSAM_RID_ENUMERATION EnumerationBuffer = NULL;
+ ULONG PreferedMaximumLength;
+ ULONG RidIndex;
+ ULONG NameIndex;
+ ULONG UserAccountControl;
+ LSA_HANDLE PolicyHandle = NULL;
+ PUNICODE_STRING SamDomainAccountNames = NULL;
+ ULONG SamDomainAccountNamesLength;
+ PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains = NULL;
+ PLSA_TRANSLATED_SID Sids = NULL;
+ PSID_NAME_USE Uses = NULL;
+ PULONG RelativeIds = NULL;
+ ULONG RelativeIdsLength;
+ PUNICODE_STRING ThrowawayNames = NULL;
+ LONG ConstantDomainIndex;
+ LONG DomainIndex;
+
+ //
+ // Setup Object Attributes for connecting to SAM on the specified
+ // DomainController.
+ //
+
+ CtLsaInitObjectAttributes(
+ &SamObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ //
+ // Connect to the SAM server.
+ //
+
+ Status = SamConnect(
+ DomainControllerName,
+ &SamServerHandle,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ &SamObjectAttributes
+ );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Built in Domain failed\n"
+ "... SamConnect returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Lookup the SAM Domain by its name.
+ //
+
+ Status = SamLookupDomainInSamServer(
+ SamServerHandle,
+ SamDomainName,
+ &SamDomainSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... SamLookupDomainInSamServer returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Verify that a Domain Sid has been returned.
+ //
+
+ if (SamDomainSid == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Alias Name Lookup in SAM Domain failed\n"
+ ".. no DomainSid returned\n"
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Open the SAM Domain
+ //
+
+ Status = SamOpenDomain(
+ SamServerHandle,
+ DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
+ SamDomainSid,
+ &SamDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Alias Name Lookup in SAM Domain failed\n"
+ "... SamOpenDomain returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Enumerate the accounts of the specified type the SAM Domain
+ //
+
+ CountReturned = 0;
+ EnumerationContext = 0;
+ EnumerationBuffer = NULL;
+ PreferedMaximumLength = 512;
+
+
+ switch (SamAccountType) {
+
+ case CT_SAM_ALIAS:
+
+ Status = SamEnumerateAliasesInDomain(
+ SamDomainHandle,
+ &EnumerationContext,
+ (PVOID *) &EnumerationBuffer,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Alias Name Lookup in SAM Domain failed\n"
+ "... SamEnumerateAliasInDomain returned 0x%lx\n",
+ Status
+ );
+ }
+
+ break;
+
+ case CT_SAM_GROUP:
+
+ Status = SamEnumerateGroupsInDomain(
+ SamDomainHandle,
+ &EnumerationContext,
+ (PVOID *) &EnumerationBuffer,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Group Name Lookup in SAM Domain failed\n"
+ "... SamEnumerateGroupsInDomain returned 0x%lx\n",
+ Status
+ );
+ }
+
+ break;
+
+ case CT_SAM_USER:
+
+ UserAccountControl = 0;
+
+ Status = SamEnumerateUsersInDomain(
+ SamDomainHandle,
+ &EnumerationContext,
+ UserAccountControl,
+ (PVOID *) &EnumerationBuffer,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General User Name Lookup in SAM Domain failed\n"
+ "... SamEnumerateUsersInDomain returned 0x%lx\n",
+ Status
+ );
+ }
+
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ if (CountReturned == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... SamEnumerate<AccountType>InDomain returned no Alias Ids\n"
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Verify that an EnumerationBuffer has been returned.
+ //
+
+ if (EnumerationBuffer == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Alias Name Lookup in SAM Built in Domain failed\n"
+ "... no EnumerationBuffer structure returned\n"
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Construct an array of account names to be looked up.
+ //
+
+ SamDomainAccountNamesLength = CountReturned * sizeof ( UNICODE_STRING );
+
+ SamDomainAccountNames = LocalAlloc( (UINT) LMEM_FIXED, (UINT) SamDomainAccountNamesLength );
+
+ if (SamDomainAccountNames == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... Unable to allocate memory for Sam Domain Account Names\n"
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Fish the account names out of the Enumeration Buffer returned by
+ // SamEnumerateIdsInDomain.
+ //
+
+ for ( NameIndex = 0; NameIndex < CountReturned; NameIndex++ ) {
+
+ if (EnumerationBuffer[ NameIndex].Name.Length == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Names in Sam Domain failed\n"
+ "SamEnumerateIdsInDomain returned 0 length Unicode Name\n",
+ "for id %d\n",
+ NameIndex
+ );
+
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ if (EnumerationBuffer[ NameIndex].Name.Buffer == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Names in Sam Domain failed\n"
+ "SamEnumerateIdsInDomain returned NULL Unicode Name Buffer\n",
+ "for id %d\n",
+ NameIndex
+ );
+
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ SamDomainAccountNames[NameIndex].Buffer = EnumerationBuffer[ NameIndex].Name.Buffer;
+ SamDomainAccountNames[NameIndex].Length = EnumerationBuffer[ NameIndex].Name.Length;
+ SamDomainAccountNames[NameIndex].MaximumLength = EnumerationBuffer[ NameIndex].Name.MaximumLength;
+ }
+
+ if (!BooleanStatus) {
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Now Lookup these account names via LsaLookupNames specifying the
+ // Workstation. First, we need to open its Policy object.
+ //
+
+ CtLsaInitObjectAttributes(
+ &LsaObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ WorkstationName,
+ &LsaObjectAttributes,
+ POLICY_LOOKUP_NAMES,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sids in Sam Domain failed\n"
+ "LsaOpenPolicy for Workstation returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ ReferencedDomains = NULL;
+ Sids = NULL;
+
+ Status = LsaLookupNames(
+ PolicyHandle,
+ CountReturned,
+ SamDomainAccountNames,
+ &ReferencedDomains,
+ &Sids
+ );
+
+ if (Status != STATUS_SUCCESS) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain\n"
+ "... LsaLookupNames failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Verify that a ReferencedDomains structure has been returned.
+ //
+
+ if (ReferencedDomains == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... no ReferencedDomains structure returned\n");
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Verify that a Sids array has been returned.
+ //
+
+ if (Sids == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... no Sids array returned\n"
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Compare the Account Rids returned from SAM directly with the
+ // Sid translation info returned from LsaLookupNames.
+ //
+
+ for ( RidIndex = 0; RidIndex < CountReturned; RidIndex++ ) {
+
+ if (EnumerationBuffer[ RidIndex ].RelativeId !=
+ Sids[ RidIndex ].RelativeId) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... mismatch on RelativeId number %d\n"
+ "... expected 0x%lx, got 0x%lx\n",
+ RidIndex,
+ EnumerationBuffer[ RidIndex ].RelativeId,
+ Sids[ RidIndex ].RelativeId
+ );
+
+ BooleanStatus = FALSE;
+ break;
+
+
+ }
+ }
+
+ if (!BooleanStatus) {
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Construct an array of the Relative Ids returned by
+ // SamEnumerate<AccountType>InDomain. We will look them up
+ // to obtain their Uses.
+ //
+
+ RelativeIdsLength = CountReturned * sizeof( ULONG );
+
+ RelativeIds = LocalAlloc( (UINT) LMEM_FIXED, (UINT) RelativeIdsLength);
+
+ if (RelativeIds == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... Unable to allocate memory for Rid array\n"
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ for (RidIndex = 0; RidIndex < CountReturned; RidIndex++) {
+
+ RelativeIds[ RidIndex ] = EnumerationBuffer[ RidIndex ].RelativeId;
+ }
+
+ //
+ // Now lookup the RelativeIds in the Sam Domain and get their Uses.
+ //
+
+ Status = SamLookupIdsInDomain(
+ SamDomainHandle,
+ CountReturned,
+ RelativeIds,
+ &ThrowawayNames,
+ &Uses
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... SamLookupIdsInDomain returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ if (ThrowawayNames == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... SamLookupIdsInDomain returned NULL Uames array\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ if (Uses == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Domain failed\n"
+ "... SamLookupIdsInDomain returned NULL Uses array\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Now compare the Uses returned from SAM with the Uses returned from
+ // LsaLookupNames.
+ //
+
+ for (RidIndex = 0; RidIndex < CountReturned; RidIndex++) {
+
+ if (Uses[ RidIndex] != Sids[ RidIndex].Use) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... mismatch on Use returned for Name %d\n"
+ "... expected %d, got %d\n"
+ ".. no further Use values compared\n",
+ (ULONG) Uses[ RidIndex ],
+ (ULONG) Sids[RidIndex].Use
+ );
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+
+ if (!BooleanStatus) {
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // Finally, verify the Trust Information entry for this domain and
+ // check that each Sid translation structure references that domain.
+ //
+
+ try {
+
+ //
+ // First, load the Domain Index returned in the first Sid
+ // Translation structure and verify that it is non-negative.
+ //
+
+ ConstantDomainIndex = Sids[ 0 ].DomainIndex;
+
+ if (ConstantDomainIndex < 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... negative DomainIndex Returned\n"
+ "... no further Domain Indices checked\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // Now verify that the referenced Domain List structure returned
+ // contains the correct Trust Information.
+ //
+
+ if (!RtlEqualSid(
+ SamDomainSid,
+ ReferencedDomains->Domains[ ConstantDomainIndex ].Sid
+ )) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... Incorrect Sid in the single ReferencedDomainList entry\n"
+ "... Sid should match Sid of the SAM Domain we're searching\n"
+ );
+
+ BooleanStatus = FALSE;
+
+ }
+
+
+ if (!RtlEqualUnicodeString(
+ SamDomainName,
+ &ReferencedDomains->Domains[ ConstantDomainIndex].Name,
+ TRUE)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... Incorrect name in the single ReferencedDomainList entry\n"
+ "... name should match name of SAM domain we're searching\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (BooleanStatus) {
+
+ for (RidIndex = 0; RidIndex < CountReturned; RidIndex++) {
+
+ DomainIndex = Sids[ RidIndex ].DomainIndex;
+
+ //
+ // First verify that a non-negative Domain Index was returned.
+ //
+
+ if (DomainIndex < 0) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... negative DomainIndex Returned\n"
+ "... no further Domain Indices checked\n"
+ );
+
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ if (DomainIndex != ConstantDomainIndex) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... all domain indices not the same\n"
+ "... for ids all in the same SAM domain\n"
+ "... no further Domain Indices checked\n"
+ );
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+ }
+
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... access violation when examining output\n"
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (!BooleanStatus) {
+
+ goto LookupNamesInSamDomainError;
+ }
+
+
+LookupNamesInSamDomainFinish:
+
+ //
+ // If necessary, close the SAM Built In Domain Handle for the Workstation.
+ //
+
+ if (SamDomainHandle != NULL) {
+
+ Status = SamCloseHandle( SamDomainHandle);
+ SamDomainHandle = NULL;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... SamCloseHandle( SamDomainHandle returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+
+ //
+ // If necessary, disconnect from the Workstation's SAM Server.
+ //
+
+ if (SamServerHandle != NULL) {
+
+ Status = SamCloseHandle( SamServerHandle );
+
+ SamServerHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... SamCloseHandle returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+ }
+
+ //
+ // If necessary, close the LSA handle to the Workstation's Policy object.
+ //
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose( PolicyHandle );
+
+ PolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - General Name Lookup in SAM Account Domain failed\n"
+ "... LsaClose( PolicyHandle ) returned 0x%lx\n",
+ Status
+ );
+
+ goto LookupNamesInSamDomainError;
+ }
+ }
+
+ return(BooleanStatus);
+
+LookupNamesInSamDomainError:
+
+ BooleanStatus = FALSE;
+
+ goto LookupNamesInSamDomainFinish;
+}
+
+
+BOOLEAN
+CtLsaLookupConfigure(
+ IN OPTIONAL PUNICODE_STRING WorkstationName,
+ IN OPTIONAL PUNICODE_STRING PrimaryDomainName,
+ IN PSID PrimaryDomainSid,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount,
+ IN OPTIONAL PUNICODE_STRING TrustedDomainNames,
+ IN PSID *TrustedDomainSids,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN ULONG TrustedDomainCtrlrTotal,
+ IN PULONG TrustedDomainCtrlrCounts
+ )
+
+/*++
+
+Routine Description:
+
+ This function configures the LSA and SAM Databases for multiple
+ machines for a Sid or Name Lookup Test. These machines may
+ be combined into one if desired. The machines are setup to
+ represent the following:
+
+ - A Workstation
+ - One or more Primary Domain Controllers
+ - One or more Trusted Domain Controllers
+
+Arguments:
+
+ WorkstationName - Pointer to name of the workstation.
+
+ PrimaryDomainName - Pointer to Unicode String containing name
+ of the Primary Domain (if any) for the Workstation. If NULL
+ is specified, no Primary Domain will be set up. If a zero
+ length Unicode name is specified, a default Primary Domain name
+ will be used.
+
+ PrimaryDomainSid - Pointer to Sid for the Primary Domain.
+
+ PrimaryDomainCtrlrNames - Pointer to array of Unicode Strings
+ specifying the names of machines that form the set of Controllers
+ for the Primary Domain. The first of these is set to be the
+ Primary Controller, the remainder are the Backup Controllers.
+
+ PrimaryDomainCtrlrCount - Count of Domain Controllers provided
+ in PrimaryDomainCtrlrNames.
+
+ TrustedDomainNames - Pointer to array of Unicode Strings containing
+ names of Domains that are Trusted by the Primary Domain for
+ authentication.
+
+ TrustedDomainSids - Pointer to an array of Sids for the Trusted Domains.
+
+ TrustedDomainCount - Count of Trusted Domain Names provided on
+ the TrustedDomainNames parameter.
+
+ TrustedDomainCtrlrNames - Pointer to array of Unicode Strings containing
+ the names of machines that are to act as Primary or Backup Domain
+ Controllers for the Trusted Domains specified on TrustedDomainNames.
+ This array is subdivided into subarrays, there being one subarray
+ corresponding to each TrustedDomainNames entry. Each subarray
+ contains the name of the Primary Controller followed by the names
+ of the Backup Domain Controllers for the Trusted Domain it describes.
+ The size of the subarray for TrustedDomain n is in element n of the
+ TrustedDomainCtrlrCounts array.
+
+ TrustedDomainCtrlrTotal - Count of the total number of Domain
+ Controllers provided in TrustedDomainCtrlrNames.
+
+ TrustedDomainCtrlrCounts - Pointer to array in which the nth element
+ contains the number of Domain Controllers for Trusted Domain n.
+
+Return Values:
+
+ BOOLEAN - TRUE if the configuration was successful, else FALSE.
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = FALSE;
+ PULONG RelativeId = NULL;
+ PSID Sid = NULL;
+
+ //
+ // Print out the configuration specified on the ctlsarpc command line.
+ //
+
+ if (!CtLsaLookupPrintConfiguration(
+ WorkstationName,
+ PrimaryDomainName,
+ PrimaryDomainCtrlrNames,
+ PrimaryDomainCtrlrCount,
+ TrustedDomainNames,
+ TrustedDomainCount,
+ TrustedDomainCtrlrNames,
+ TrustedDomainCtrlrCounts
+ )) {
+
+ goto LookupConfigureError;
+ }
+
+ //
+ // Configure the Workstation. We store information about the Primary
+ // Domain and its list of Domain Controllers in the LSA Database.
+ //
+
+ if (!CtLsaLookupConfigureWksta(
+ WorkstationName,
+ PrimaryDomainName,
+ PrimaryDomainSid,
+ PrimaryDomainCtrlrNames,
+ PrimaryDomainCtrlrCount
+ )) {
+
+ goto LookupConfigureError;
+ }
+
+ //
+ // Configure the Primary Domain Primary and Backup Controller (if any).
+ // machines. For each Controller, we have to set its LSA Database
+ // Primary Domain and Trusted Domain information.
+ //
+
+ if (PrimaryDomainName != NULL && PrimaryDomainName->Buffer != NULL) {
+
+ if (!CtLsaLookupConfigurePDC(
+ PrimaryDomainName,
+ PrimaryDomainSid,
+ PrimaryDomainCtrlrNames,
+ PrimaryDomainCtrlrCount,
+ TrustedDomainNames,
+ TrustedDomainSids,
+ TrustedDomainCount,
+ TrustedDomainCtrlrNames,
+ TrustedDomainCtrlrTotal,
+ TrustedDomainCtrlrCounts
+ )) {
+
+ goto LookupConfigureError;
+ }
+ }
+
+ //
+ // Configure each of the Trusted Domain Controllers (if any).
+ // Skip if the obnly Trusted Domain is the Primary Domain.
+ //
+
+ if (TrustedDomainCount > 1) {
+
+ if (!CtLsaGeneraLookupConfigureTDC(
+ TrustedDomainNames,
+ TrustedDomainSids,
+ TrustedDomainCount,
+ TrustedDomainCtrlrNames,
+ TrustedDomainCtrlrTotal,
+ TrustedDomainCtrlrCounts
+ )) {
+
+ goto LookupConfigureError;
+ }
+ }
+
+ BooleanStatus = TRUE;
+
+LookupConfigureFinish:
+
+ return(BooleanStatus);
+
+LookupConfigureError:
+
+ BooleanStatus = FALSE;
+ goto LookupConfigureFinish;
+}
+
+
+VOID
+CtLsaGeneralInitUnknownName(
+ IN PUNICODE_STRING Name,
+ IN OPTIONAL PUNICODE_STRING DomainName,
+ IN OPTIONAL PSID DomainSid,
+ OUT PCT_UNKNOWN_NAME_ENTRY UnknownNameInfo,
+ IN BOOLEAN DomainKnown
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets up information for an Unknown Name. The Name may be
+ completely unknown, or its DomainName may be recognizable.
+
+Arguments:
+
+ Name - Pointer to the terminal part of the name, i.e. without any
+ Domain Name prefix.
+
+ DomainName - Optional pointer to Domain Name.
+
+ DomainSid - Optional pointer to Domain Sid.
+
+ UnknownNameInfo - Pointer to structure that will receive the Name,
+ Domain Name, Domain Sid information.
+
+ DomainKnown - Indicates whether the optional Domain Name and Domain Sid
+ are known. Here "known" means that they are one of the following
+ domains:
+
+ The Workstation's SAM BUILTIN or Accounts Domains
+
+ The Workstation's Primary Domain's SAM Accounts Domain
+
+ The SAM Accounts Domain on any Domain that is Trusted by the
+ Workstation's Primary Domain.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //
+ // Just copy the supplied information to the UnknownNameInfo structure.
+ //
+
+ UnknownNameInfo->Name = *Name;
+
+ if (DomainName != NULL) {
+
+ UnknownNameInfo->DomainName = *DomainName;
+
+ } else {
+
+ UnknownNameInfo->DomainName.Buffer = NULL;
+ UnknownNameInfo->DomainName.Length = 0;
+ UnknownNameInfo->DomainName.MaximumLength = 0;
+ }
+
+ UnknownNameInfo->DomainSid = DomainSid;
+
+ UnknownNameInfo->DomainKnown = DomainKnown;
+}
+
+
+BOOLEAN
+CtLsaLookupConfigureWksta(
+ IN PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING PrimaryDomainName,
+ IN PUNICODE_STRING PrimaryDomainSid,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function configures a Workstation's Policy Database by storing
+ information defining the Workstation's Primary Domain and list
+ of that domain's Domain Controllers.
+
+Arguments:
+
+ WorkstationName - Pointer to Unicode Name of the target workstation
+ (\\machinename).
+
+ PrimaryDomainName - Pointer to Unicode String containing name
+ of the Primary Domain (if any) for the Workstation. If NULL
+ is specified, no Primary Domain will be set up. If a zero
+ length Unicode name is specified, a default Primary Domain name
+ will be used.
+
+ PrimaryDomainSid - Pointer to the Sid of the Primary Domain.
+
+ PrimaryDomainCtrlrNames - Pointer to array of Unicode Strings
+ specifying the names of machines that form the set of Controllers
+ for the Primary Domain. The first of these is set to be the
+ Primary Controller, the remainder are the Backup Controllers.
+
+ PrimaryDomainCtrlrCount - Count of Domain Controllers provided
+ in PrimaryDomainCtrlrNames.
+
+Return Values:
+
+ BOOLEAN - TRUE if configuration successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandle;
+ POLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ POLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo;
+ LSA_HANDLE PrimaryDomainHandle = NULL;
+ LSA_HANDLE TrustedDomainHandle = NULL;
+ LSA_TRUST_INFORMATION PrimaryDomainTrustInformation;
+ TRUSTED_CONTROLLERS_INFO PrimaryDomainTrustedCtrlrsInfo;
+
+ if (WorkstationName == NULL || WorkstationName->Length == 0) {
+
+ DbgPrint("LSA RPC CT - Configuring Workstation (Local Machine)\n");
+
+ } else {
+
+ DbgPrint("LSA RPC CT - Configuring Workstation %Z\n", WorkstationName);
+ }
+
+ //
+ // Open a handle to the Workstation's Policy Database.
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ WorkstationName,
+ &ObjectAttributes,
+ POLICY_TRUST_ADMIN | POLICY_SERVER_ADMIN,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test Configuration failed\n"
+ "... (Configuring the Workstation)\n"
+ "... Policy object open handle for Wksta failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureWkstaError;
+ }
+
+ //
+ // Now configure the SAM Account Domain Name. This is set equal
+ // to the fixed name "ACCOUNTS" as we assume that the target
+ // Workstation is configured as a Workstation, not a Server, i.e.
+ // that the Product Type is Win-NT. The Sid of this domain is
+ // configurable, so for this test configuration, we supply one that we
+ // generate ourselves.
+ //
+
+ RtlInitUnicodeString(
+ &PolicyAccountDomainInfo.DomainName,
+ L"ACCOUNTS"
+ );
+
+ PolicyAccountDomainInfo.DomainSid = AccountDomainSid;
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Workstation)\n"
+ ".. LsaSetInformationPolicy for Account Domain failed "
+ "0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureWkstaError;
+ }
+
+ //
+ // Now set the Name and Sid for the workstation's Primary Domain.
+ //
+
+ PolicyPrimaryDomainInfo.Name = *PrimaryDomainName;
+ PolicyPrimaryDomainInfo.Sid = PrimaryDomainSid;
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandle,
+ PolicyPrimaryDomainInformation,
+ &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test Configuration failed\n"
+ "... (Configuring the Workstation)\n"
+ ".. LsaSetInformationPolicy setting Primary Domain failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureWkstaError;
+ }
+
+ //
+ // Create a TrustedDomain object for the Primary Domain.
+ //
+
+ PrimaryDomainTrustInformation.Name = *PrimaryDomainName;
+ PrimaryDomainTrustInformation.Sid = PrimaryDomainSid;
+ PrimaryDomainTrustedCtrlrsInfo.Entries = PrimaryDomainCtrlrCount;
+ PrimaryDomainTrustedCtrlrsInfo.Names = PrimaryDomainCtrlrNames;
+
+ Status = CtLsaLookupConfigureTrustedDomain(
+ PolicyHandle,
+ &PrimaryDomainTrustInformation,
+ &PrimaryDomainTrustedCtrlrsInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupConfigureWkstaError;
+ }
+
+ BooleanStatus = TRUE;
+
+LookupConfigureWkstaFinish:
+
+ return(BooleanStatus);
+
+LookupConfigureWkstaError:
+
+ BooleanStatus = FALSE;
+ goto LookupConfigureWkstaFinish;
+}
+
+
+BOOLEAN
+CtLsaLookupConfigurePDC(
+ IN PUNICODE_STRING PrimaryDomainName,
+ IN PSID PrimaryDomainSid,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount,
+ IN PUNICODE_STRING TrustedDomainNames,
+ IN PSID *TrustedDomainSids,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN ULONG TrustedDomainCtrlrTotal,
+ IN PULONG TrustedDomainCtrlrCounts
+ )
+
+/*++
+
+Routine Description:
+
+ This function configures the Policy Database for a set of machines
+ so that they represent Domain Controllers for the Primary Domain
+ of a Workstation. For each Controller, the Policy Lsa Server Role
+ information is set and the name of the SAM Account domain is set to
+ the name of the Primary Domain. In addition, a Trusted Domain Object is
+ created for the Primary Domain and it is initilaized with the list
+ of Controllers.
+
+Arguments:
+
+ PrimaryDomainName - Pointer to Unicode String containing name
+ of the Primary Domain (if any) for the Workstation. If NULL
+ is specified, no Primary Domain will be set up. If a zero
+ length Unicode name is specified, a default Primary Domain name
+ will be used.
+
+ PrimaryDomainSid - Pointer to the Sid of the Primary Domain.
+
+ PrimaryDomainCtrlrNames - Pointer to array of Unicode Strings
+ specifying the names of machines that form the set of Controllers
+ for the Primary Domain. The first of these is set to be the
+ Primary Controller, the remainder are the Backup Controllers.
+
+ PrimaryDomainCtrlrCount - Count of Domain Controllers provided
+ in PrimaryDomainCtrlrNames.
+
+ TrustedDomainNames - Pointer to array of Unicode Strings containing
+ names of Domains that are Trusted by the Primary Domain for
+ authentication.
+
+ TrustedDomainSids - Pointer to an array of TrustedDomainCount Sids
+ for the TrustedDomains
+
+ TrustedDomainCount - Count of Trusted Domain Names/Sids provided on
+ the TrustedDomainNames/Sids parameters.
+
+ TrustedDomainCtrlrNames - Pointer to array of Unicode Strings containing
+ the names of machines that are to act as Primary or Backup Domain
+ Controllers for the Trusted Domains specified on TrustedDomainNames.
+ This array is subdivided into subarrays, there being one subarray
+ corresponding to each TrustedDomainNames entry. Each subarray
+ contains the name of the Primary Controller followed by the names
+ of the Backup Domain Controllers for the Trusted Domain it describes.
+ The size of the subarray for TrustedDomain n is in element n of the
+ TrustedDomainCtrlrCounts array.
+
+ TrustedDomainCtrlrTotal - Count of the total number of Domain
+ Controllers provided in TrustedDomainCtrlrNames.
+
+ TrustedDomainCtrlrCounts - Pointer to array in which the nth element
+ contains the number of Domain Controllers for Trusted Domain n.
+
+Return Values:
+
+ BOOLEAN - TRUE if configuration successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus;
+ ULONG DomainControllerIndex;
+ LSA_HANDLE TrustedDomainHandle = NULL;
+ POLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandle;
+ ULONG FirstDomainCtrlrIndex;
+ POLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ ULONG DomainNumber;
+ LSA_TRUST_INFORMATION DomainTrustInformation;
+ TRUSTED_CONTROLLERS_INFO DomainTrustedCtrlrsInfo;
+ LSA_TRUST_INFORMATION PrimaryDomainTrustInformation;
+ TRUSTED_CONTROLLERS_INFO PrimaryDomainTrustedCtrlrsInfo;
+
+ //
+ // Iterate over the set of Domain Controllers for the Primary Domain.
+ //
+
+ for (DomainControllerIndex = 0;
+ DomainControllerIndex < PrimaryDomainCtrlrCount;
+ DomainControllerIndex++) {
+
+ //
+ // Select the Policy Lsa Server Role. This is Primary for the
+ // first controller for a Domain, backup for all others.
+ //
+
+ PolicyLsaServerRoleInfo = PolicyServerRoleBackup;
+
+ if (DomainControllerIndex == 0) {
+
+ PolicyLsaServerRoleInfo = PolicyServerRolePrimary;
+ DbgPrint(
+ "LSA RPC CT - Configuring %Z as Primary Controller"
+ " for Primary Domain\n",
+ &PrimaryDomainCtrlrNames[DomainControllerIndex]
+ );
+
+ } else {
+
+ DbgPrint(
+ "LSA RPC CT - Configuring %Z as Backup Controller"
+ " for Primary Domain\n",
+ &PrimaryDomainCtrlrNames[DomainControllerIndex]
+ );
+ }
+
+ //
+ // Open a handle to the Policy Object for the next
+ // Domain Controller for the Primary Domain.
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ &PrimaryDomainCtrlrNames[DomainControllerIndex],
+ &ObjectAttributes,
+ POLICY_TRUST_ADMIN | POLICY_SERVER_ADMIN,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test Configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ "LSA RPC CT - Policy object open handle %d failed 0x%lx\n",
+ Status
+ );
+ }
+
+ //
+ // Now set the Server Role.
+ //
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandle,
+ PolicyLsaServerRoleInformation,
+ &PolicyLsaServerRoleInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ ".. LsaSetInformationPolicy for Server Role failed 0x%lx\n",
+ Status
+ );
+
+ break;
+ }
+
+ //
+ // Now configure the SAM Account Domain Name. This is set equal
+ // to the Primary Domain Name.
+ //
+
+ PolicyAccountDomainInfo.DomainName = *PrimaryDomainName;
+ PolicyAccountDomainInfo.DomainSid = PrimaryDomainSid;
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ ".. LsaSetInformationPolicy for Account Domain failed "
+ "0x%lx\n",
+ Status
+ );
+
+ break;
+ }
+
+ //
+ // Now create a Trusted Domain Object for the Primary Domain.
+ //
+
+ PrimaryDomainTrustInformation.Name = *PrimaryDomainName;
+ PrimaryDomainTrustInformation.Sid = PrimaryDomainSid;
+ PrimaryDomainTrustedCtrlrsInfo.Entries = PrimaryDomainCtrlrCount;
+ PrimaryDomainTrustedCtrlrsInfo.Names = PrimaryDomainCtrlrNames;
+
+ Status = CtLsaLookupConfigureTrustedDomain(
+ PolicyHandle,
+ &PrimaryDomainTrustInformation,
+ &PrimaryDomainTrustedCtrlrsInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupConfigurePDCError;
+ }
+
+ //
+ // For each of the other Trusted Domains (if any), create a Trusted Domain object
+ // and store the Controller Information within it.
+ //
+
+ //
+ // Set index to skip past TDC's for Primary Domain.
+ //
+
+ FirstDomainCtrlrIndex = TrustedDomainCtrlrCounts[0];
+
+ for (DomainNumber = 1; DomainNumber < TrustedDomainCount; DomainNumber++) {
+
+ DomainTrustInformation.Name = TrustedDomainNames[DomainNumber];
+ DomainTrustInformation.Sid = TrustedDomainSids[DomainNumber];
+ DomainTrustedCtrlrsInfo.Entries = TrustedDomainCtrlrCounts[DomainNumber];
+ DomainTrustedCtrlrsInfo.Names =
+ &TrustedDomainCtrlrNames[FirstDomainCtrlrIndex];
+
+ Status = CtLsaLookupConfigureTrustedDomain(
+ PolicyHandle,
+ &DomainTrustInformation,
+ &DomainTrustedCtrlrsInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Obtain the index of the first Domain Controller for the next
+ // domain by adding in the count of controllers in the domain
+ // just dealt with.
+ //
+
+ FirstDomainCtrlrIndex += TrustedDomainCtrlrCounts[ DomainNumber ];
+
+ if (FirstDomainCtrlrIndex > TrustedDomainCtrlrTotal) {
+
+ DbgPrint(
+ "LSA RPC CT Lookup Sids/Names Test Configuration failed\n"
+ "... Trusted Domain Ctrlr Total less than sum of\n"
+ "... Trusted Domain Ctrlr Counts\n"
+ );
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupConfigurePDCError;
+ }
+
+ BooleanStatus = TRUE;
+
+LookupConfigurePDCFinish:
+
+ return(BooleanStatus);
+
+LookupConfigurePDCError:
+
+ BooleanStatus = FALSE;
+ goto LookupConfigurePDCFinish;
+}
+
+
+NTSTATUS
+CtLsaLookupConfigureTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN PLSA_TRUST_INFORMATION TrustedDomainInformation,
+ IN PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a Trusted Domain object (if necessary) and
+ initializes it with a Domain Name, Sid and Trusted Controller List.
+
+Arguments:
+
+ PolicyHandle - Handle to LSA Policy Object. This handle must have
+ POLICY_TRUST_ADMIN access granted.
+
+ TrustedDomainInformation - Pointer to Trust Information for the Domain
+ consisting of the Unicode Domain Name and Sid.
+
+ TrustedControllersInfo - Pointer to list of Trusted Controller Names.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Status returned by LsaCreateTrustedDomain or
+ LsaSetInformationTrustedDomain.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSA_HANDLE TrustedDomainHandle = NULL;
+ ULONG ControllerNumber;
+
+ DbgPrint(
+ "... Configuring Trusted Domain %Z\n",
+ &TrustedDomainInformation->Name
+ );
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ TrustedDomainInformation,
+ TRUSTED_SET_CONTROLLERS,
+ &TrustedDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_OBJECT_NAME_COLLISION) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ ".. LsaCreateTrustedDomain failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureTrustedDomainError;
+
+ } else {
+
+ //
+ // Open the existing Trusted Domain object with the given Sid.
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainInformation->Sid,
+ TRUSTED_SET_CONTROLLERS,
+ &TrustedDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ ".. LsaOpenTrustedDomain failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureTrustedDomainError;
+ }
+ }
+ }
+
+ //
+ // Print out the names of all the controllers.
+ //
+
+ for (ControllerNumber = 0;
+ ControllerNumber < TrustedControllersInfo->Entries;
+ ControllerNumber ++
+ ) {
+
+ DbgPrint(
+ "...... Trusted Controller %d - %Z\n",
+ ControllerNumber,
+ &TrustedControllersInfo->Names[ControllerNumber]
+ );
+ }
+
+ //
+ // Setup info for this Trusted Domain.
+ //
+
+ Status = LsaSetInformationTrustedDomain(
+ TrustedDomainHandle,
+ TrustedControllersInformation,
+ TrustedControllersInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sids/Name Test configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ "... LsaSetInformationTrustedDomain for Domain Ctrlrs failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureTrustedDomainError;
+ }
+
+ //
+ // Close the Trusted Domain Handle
+ //
+
+ Status = LsaClose( TrustedDomainHandle );
+
+ TrustedDomainHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ "... LsaClose for TrustedDomain object failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureTrustedDomainError;
+ }
+
+LookupConfigureTrustedDomainFinish:
+
+ //
+ // If necessary, close the Trusted Domain object handle
+ //
+
+ if (TrustedDomainHandle != NULL) {
+
+ Status = LsaClose( TrustedDomainHandle );
+
+ TrustedDomainHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Primary Domain Controllers)\n"
+ "... LsaClose for TrustedDomain object failed 0x%lx\n",
+ Status
+ );
+
+ goto LookupConfigureTrustedDomainError;
+ }
+ }
+
+ return(Status);
+
+LookupConfigureTrustedDomainError:
+
+ goto LookupConfigureTrustedDomainFinish;
+}
+
+
+
+BOOLEAN
+CtLsaGeneraLookupConfigureTDC(
+ IN PUNICODE_STRING TrustedDomainNames,
+ IN PSID *TrustedDomainSids,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN ULONG TrustedDomainCtrlrTotal,
+ IN PULONG TrustedDomainCtrlrCounts
+ )
+
+/*++
+
+Routine Description:
+
+ This function configures the Policy Databases for all of the machines
+ that are to act as Trusted Domain Controllers. For each machine,
+ the Policy Lsa Server Role Info is set to Primary or Backup.
+
+ TrustedDomainNames - Pointer to array of Unicode Strings containing
+ names of Domains that are Trusted by the Primary Domain for
+ authentication.
+
+ TrustedDomainSids - Pointer to an array of TrustedDomainCount Sids
+ for the TrustedDomains
+
+ TrustedDomainCount - Count of Trusted Domain Names/Sids provided on
+ the TrustedDomainNames/Sids parameters.
+
+ TrustedDomainCtrlrNames - Pointer to array of Unicode Strings containing
+ the names of machines that are to act as Primary or Backup Domain
+ Controllers for the Trusted Domains specified on TrustedDomainNames.
+ This array is subdivided into subarrays, there being one subarray
+ corresponding to each Trusted Domain Controller. Each subarray
+ contains the name of the Primary Controller followed by the names
+ of the Backup Domain Controllers for the Trusted Domain it describes.
+ The size of the subarray for TrustedDomain n is in element n of the
+ TrustedDomainCtrlrCounts array.
+
+ TrustedDomainCtrlrTotal - Total number of Domain Controllers provided
+ in TrustedDomainCtrlrNames.
+
+ TrustedDomainCtrlrCounts - Pointer to array in which the nth element
+ contains the number of Domain Controllers for Trusted Domain n.
+
+Return Values:
+
+ BOOLEAN - TRUE if configuration successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus;
+ ULONG DomainControllerIndex;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandle;
+ ULONG DomainNumber;
+ ULONG DomainControllerNumber;
+ POLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo;
+ POLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+
+ //
+ // Scan the list of Domain Controllers. For each one, open its
+ // Policy Object and set the Policy Lsa Server Role Information.
+ //
+
+ DomainNumber = 0;
+ DomainControllerIndex = 0;
+
+ for (DomainControllerIndex = 0;
+ DomainControllerIndex < TrustedDomainCtrlrTotal;
+ DomainControllerIndex++) {
+
+ //
+ // If there are no Domain Controllers for this domain, skip.
+ //
+
+ while ((TrustedDomainCtrlrCounts[DomainNumber] == 0) &&
+ (DomainNumber < TrustedDomainCount)) {
+
+ DomainNumber++;
+ }
+
+ //
+ // If all Domains have been dealt with, break out.
+ //
+
+ if (DomainNumber >= TrustedDomainCount) {
+
+ break;
+ }
+
+ //
+ // If the Count of Controllers for the current domain has been
+ // exceeded, this Controller is for the next domain. Reset the
+ // Controller Number to 0.
+ //
+
+ if (DomainControllerNumber >= TrustedDomainCtrlrCounts[ DomainNumber ]) {
+
+ DomainControllerNumber = 0;
+ }
+
+ //
+ // Select the Policy Lsa Server Role. This is Primary for the
+ // first controller for a Domain, backup for all others.
+ //
+
+ PolicyLsaServerRoleInfo = PolicyServerRoleBackup;
+
+ if (DomainControllerNumber == 0) {
+
+ PolicyLsaServerRoleInfo = PolicyServerRolePrimary;
+ }
+
+ //
+ // Open a handle to the Policy Object
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ PolicyHandle = NULL;
+
+ Status = LsaOpenPolicy(
+ &TrustedDomainCtrlrNames[DomainControllerIndex],
+ &ObjectAttributes,
+ POLICY_TRUST_ADMIN | POLICY_SERVER_ADMIN,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Trusted Domain Controllers)\n"
+ "... Policy object open handle %d failed 0x%lx\n",
+ Status
+ );
+ }
+
+ //
+ // Now set the Server Role.
+ //
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandle,
+ PolicyLsaServerRoleInformation,
+ &PolicyLsaServerRoleInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup configuration failed\n"
+ "... (Configuring the Trusted Domain Controllers)\n"
+ "... LsaSetInformationPolicy for Server Role failed 0x%lx\n",
+ Status
+ );
+
+ break;
+ }
+
+ //
+ // Now configure the SAM Account Domain Name. This is set equal
+ // to the Trusted Domain Name.
+ //
+
+ RtlInitUnicodeString(
+ &PolicyAccountDomainInfo.DomainName,
+ TrustedDomainNames[DomainNumber].Buffer
+ );
+
+ PolicyAccountDomainInfo.DomainSid = TrustedDomainSids[DomainNumber];
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Lookup Sid/Name Test configuration failed\n"
+ "... (Configuring the Trusted Domain Controllers)\n"
+ "... LsaSetInformationPolicy for Account Domain failed 0x%lx\n",
+ Status
+ );
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupConfigureTDCError;
+ }
+
+ BooleanStatus = TRUE;
+
+LookupConfigureTDCFinish:
+
+ return(BooleanStatus);
+
+LookupConfigureTDCError:
+
+ BooleanStatus = FALSE;
+
+ goto LookupConfigureTDCFinish;
+}
+
+
+BOOLEAN
+CtLsaLookupPrintConfiguration(
+ IN PUNICODE_STRING WorkstationName,
+ IN PUNICODE_STRING PrimaryDomainName,
+ IN PUNICODE_STRING PrimaryDomainCtrlrNames,
+ IN ULONG PrimaryDomainCtrlrCount,
+ IN PUNICODE_STRING TrustedDomainNames,
+ IN ULONG TrustedDomainCount,
+ IN PUNICODE_STRING TrustedDomainCtrlrNames,
+ IN PULONG TrustedDomainCtrlrCounts
+ )
+
+/*++
+
+Routine Description:
+
+ This function prints out the configuration specified on the ctlsarpc
+ command line.
+
+Arguments:
+
+ WorkstationName - Pointer to name of the workstation.
+
+ PrimaryDomainName - Pointer to Unicode String containing name
+ of the Primary Domain (if any) for the Workstation. If NULL
+ is specified, no Primary Domain will be set up. If a zero
+ length Unicode name is specified, a default Primary Domain name
+ will be used.
+
+ PrimaryDomainSid - Pointer to Sid for the Primary Domain.
+
+ PrimaryDomainCtrlrNames - Pointer to array of Unicode Strings
+ specifying the names of machines that form the set of Controllers
+ for the Primary Domain. The first of these is set to be the
+ Primary Controller, the remainder are the Backup Controllers.
+
+ PrimaryDomainCtrlrCount - Count of Domain Controllers provided
+ in PrimaryDomainCtrlrNames.
+
+ TrustedDomainNames - Pointer to array of Unicode Strings containing
+ names of Domains that are Trusted by the Primary Domain for
+ authentication.
+
+ TrustedDomainCount - Count of Trusted Domain Names provided on
+ the TrustedDomainNames parameter.
+
+ TrustedDomainCtrlrNames - Pointer to array of Unicode Strings containing
+ the names of machines that are to act as Primary or Backup Domain
+ Controllers for the Trusted Domains specified on TrustedDomainNames.
+ This array is subdivided into subarrays, there being one subarray
+ corresponding to each TrustedDomainNames entry. Each subarray
+ contains the name of the Primary Controller followed by the names
+ of the Backup Domain Controllers for the Trusted Domain it describes.
+ The size of the subarray for TrustedDomain n is in element n of the
+ TrustedDomainCtrlrCounts array.
+
+ TrustedDomainCtrlrTotal - Count of the total number of Domain
+ Controllers provided in TrustedDomainCtrlrs.
+
+ TrustedDomainCtrlrCounts - Pointer to array in which the nth element
+ contains the number of Domain Controllers for Trusted Domain n.
+
+Return Values:
+
+ BOOLEAN - TRUE if successful, else FALSE.
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG ControllerNumber;
+ ULONG FirstCtrlrNumber;
+ ULONG DomainNumber;
+
+ DbgPrint("LSA RPC CT - Configuring machines for Lookup Sid/Names tests\n");
+
+ //
+ // Print Configuration Summary
+ //
+
+ DbgPrint(
+ "**************************************************************\n"
+ " Configuration Summary \n"
+ "**************************************************************\n"
+ "\n"
+ );
+
+ if (WorkstationName->Buffer != NULL) {
+
+ DbgPrint("Workstation: %Z\n\n", WorkstationName);
+
+ } else {
+
+ DbgPrint("Workstation: Local Machine\n\n");
+ }
+
+ if (PrimaryDomainName->Buffer != NULL) {
+
+ DbgPrint("Primary Domain: %Z\n", PrimaryDomainName);
+
+ for (ControllerNumber = 0;
+ ControllerNumber < PrimaryDomainCtrlrCount;
+ ControllerNumber++) {
+
+ DbgPrint(
+ "... Controller %d - %Z\n",
+ ControllerNumber,
+ &PrimaryDomainCtrlrNames[ControllerNumber]
+ );
+ }
+
+ DbgPrint("\n");
+
+ if (TrustedDomainCount == 1) {
+
+ DbgPrint("No Trusted Domains other than Primary Domain\n");
+
+ } else if (TrustedDomainCount > 1) {
+
+ ASSERT (PrimaryDomainCtrlrCount == TrustedDomainCtrlrCounts[0]);
+
+ FirstCtrlrNumber = TrustedDomainCtrlrCounts[0];
+
+ for (DomainNumber = 1;
+ DomainNumber < TrustedDomainCount;
+ DomainNumber++) {
+
+ DbgPrint(
+ "Trusted Domain %d: %Z\n",
+ DomainNumber,
+ &TrustedDomainNames[DomainNumber]
+ );
+
+ for (ControllerNumber = 0;
+ ControllerNumber < TrustedDomainCtrlrCounts[DomainNumber];
+ ControllerNumber++) {
+
+ DbgPrint(
+ "... Controller %d: %Z\n",
+ ControllerNumber,
+ &TrustedDomainCtrlrNames[ControllerNumber + FirstCtrlrNumber]
+ );
+ }
+
+ FirstCtrlrNumber += TrustedDomainCtrlrCounts[DomainNumber];
+ }
+
+ } else {
+
+ DbgPrint(
+ "LSA RPC CT - Configuration failed "
+ " - no trusted domain controllers\n"
+ );
+ BooleanStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("Workstation is Standalone\n");
+ }
+
+ DbgPrint(
+ "\n**************************************************************\n"
+ );
+
+ if (!BooleanStatus) {
+
+ goto LookupPrintConfigurationError;
+ }
+
+LookupPrintConfigurationFinish:
+
+ return(BooleanStatus);
+
+LookupPrintConfigurationError:
+
+ BooleanStatus = FALSE;
+ goto LookupPrintConfigurationFinish;
+}
+
+
+BOOLEAN
+CtLsaGeneralConstructSids(
+ OUT PSID *AccountDomainSid,
+ OUT PSID *PrimaryDomainSid,
+ OUT PSID **TrustedDomainSids,
+ IN ULONG TrustedDomainCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs a hardwired set of Sids, for an array
+ of Trusted Domains. The first of these Sids is for the Primary
+ Domain that trusts these Trusted Domains.
+
+Arguments:
+
+ AccountDomainSid - Receives a pointer to the Account Domain Sid.
+
+ PrimaryDomainSid - Receives a pointer to the Primary Domain Sid.
+ This is always the same as the first Trusted Domain Sid.
+
+ TrustedDomainSids - Receives a pointer to an array of pointers to
+ Sids, which will be used as Trusted Domain Sids.
+
+ TrustedDomainCount - Count of Trusted Domain Sids needed including
+ the primary Domain Sid.
+
+Return Value:
+
+ TRUE if successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = FALSE;
+ ULONG SidLength;
+ ULONG SidNumber;
+ PULONG RelativeId = NULL;
+ PSID OutputPrimaryDomainSid = NULL;
+ PSID *OutputDomainSids = NULL;
+ PSID Sid = NULL;
+
+ //
+ // Allocate memory for the array of Sids.
+ //
+
+ OutputDomainSids = (PSID *) LocalAlloc( LMEM_FIXED,
+ sizeof (PSID) * (TrustedDomainCount + 1)
+ );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (OutputDomainSids == NULL) {
+
+ goto GeneralConstructSidsError;
+ }
+
+ //
+ // Generate the first of the Sids. This is the Account Domain Sid.
+ //
+
+ SidLength = RtlLengthSid(LsapBuiltInDomainSid);
+
+ Sid = LocalAlloc( (UINT) LMEM_FIXED, (UINT) SidLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (Sid == NULL) {
+
+ DbgPrint("LSA RPC CT - Out of memory during Sid setup\n");
+ goto GeneralConstructSidsError;
+ }
+
+ //
+ // Make a copy of the Sid used for generating the Sids. We just
+ // select the Built-In Domain Sid. We will be generating Sids
+ // for the Primary and Trusted Domains with consecutive Rids
+ // starting with a hardwired Rid for the Primary Domain Sid. The
+ // Rids do not need to be consecutive, it is just easy to generate
+ // an array of distinct Sids that way. First copy our template
+ // Sid.
+ //
+
+ Status = RtlCopySid(
+ SidLength,
+ Sid,
+ LsapBuiltInDomainSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT RtlCopySid failed 0x%lx\n", Status);
+ goto GeneralConstructSidsError;
+ }
+
+ RelativeId = RtlSubAuthoritySid(
+ Sid,
+ *RtlSubAuthorityCountSid(Sid) - (UCHAR) 1
+ );
+
+ *RelativeId = CT_ACCOUNT_DOMAIN_RID;
+
+ for (SidNumber = 0; SidNumber < (TrustedDomainCount + 1); SidNumber++) {
+
+ OutputDomainSids[SidNumber] = LocalAlloc( (UINT) LMEM_FIXED, (UINT) SidLength );
+
+ Status = RtlCopySid(
+ SidLength,
+ OutputDomainSids[SidNumber],
+ Sid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT RtlCopySid failed 0x%lx\n", Status);
+ break;
+ }
+
+ (*RelativeId)++;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GeneralConstructSidsError;
+ }
+
+ *TrustedDomainSids = (OutputDomainSids + 1);
+ *AccountDomainSid = OutputDomainSids[0];
+ *PrimaryDomainSid = OutputDomainSids[1];
+
+ BooleanStatus = TRUE;
+
+GeneralConstructSidsFinish:
+
+ return(BooleanStatus);
+
+GeneralConstructSidsError:
+
+ BooleanStatus = FALSE;
+ goto GeneralConstructSidsFinish;
+}
+
+
+BOOLEAN
+CtLsaGeneralSetQuerySecurityObject(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaSetSecurityObject and LsaQuerySecurityObject
+ API.
+
+Parameters:
+
+ WorkstationName = Optional pointer to Unicode String specifying the
+ name of the target workstation.
+
+Return Values:
+
+ BOOLEAN - True if test successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ LSA_HANDLE PolicyHandle = NULL;
+ LSA_HANDLE SecretHandleRubble = NULL;
+ CT_SECRET_INFO SecretInfoRubble;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ PSECURITY_DESCRIPTOR ExistingSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR UpdatedSecurityDescriptor = NULL;
+ PACL Dacl = NULL;
+ PSID UpdatedOwnerSid = NULL;
+
+ DbgPrint("[1] - Test Set/Query Security Object API\n");
+
+ //
+ // First open a Handle to the LSA.
+ //
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ Status = LsaOpenPolicy(
+ WorkstationName,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION | POLICY_CREATE_SECRET,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LsaOpenPolicy returned 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectError;
+ }
+
+ //
+ // Create a new Secret called Rubble. We will Set and Query
+ // Security on this object and then delete it. First, set up Secret
+ // information for the Rubble Secret to be created.
+ //
+
+ Status = CtSecretSetInfo(
+ "Rubble",
+ "Rubble secret current value",
+ "Rubble secret old value",
+ &SecretInfoRubble
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LSA RPC CT - CtSecretSetInfo for Rubble returned 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectError;
+ }
+
+ //
+ // Create the Rubble Secret
+ //
+
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &(SecretInfoRubble.SecretName),
+ DELETE | WRITE_OWNER | WRITE_DAC | READ_CONTROL,
+ &SecretHandleRubble
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSAP RPC CT - Set/Query Security Object test failed\n"
+ "LSA RPC CT - Create Rubble Secret failed 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectError;
+ }
+
+ //
+ // First, try to assign DOMAIN_ALIAS_ADMINS as owner. This should
+ // work.
+ //
+
+ Status = CtLsaSetQuerySecurityObjectSub( SecretHandleRubble, LsapAliasAdminsSid );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSAP RPC CT - Set/Query Security Object test failed\n"
+ "LsaSetSecurityObject with DOMAIN_ALIAS_ADMINS's Sid as owner\n"
+ "returned 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectError;
+ }
+
+ //
+ // Next, try to assign WORLD as owner. We should get back
+ // STATUS_INVALID_OWNER becuae this well known group is not
+ // assignable as an owner.
+ //
+
+ Status = CtLsaSetQuerySecurityObjectSub( SecretHandleRubble, LsapWorldSid );
+
+ if (Status != STATUS_INVALID_OWNER) {
+
+ DbgPrint(
+ "LSAP RPC CT - Set/Query Security Object test failed\n"
+ "LsaSetSecurityObject with WORLD Sid as owner returned 0x%lx\n"
+ "... expected 0x%lx (STATUS_INVALID_OWNER) since WORLD\n"
+ ".. is not assignable as an owner.\n",
+ Status,
+ STATUS_INVALID_OWNER
+ );
+
+ goto SetQuerySecurityObjectError;
+ }
+
+SetQuerySecurityObjectFinish:
+
+ //
+ // If necessary, Delete the Rubble Secret.
+ //
+
+ if (SecretHandleRubble != NULL) {
+
+ Status = LsaDelete( SecretHandleRubble );
+ SecretHandleRubble = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSAP RPC CT - Set/Query Security Object test failed\n"
+ "LSA RPC CT - Delete Rubble Secret failed 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectError;
+ }
+ }
+
+ //
+ // If necessary, close the handle to the Policy Object.
+ //
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose( PolicyHandle );
+ PolicyHandle = NULL;
+
+ if (!NT_SUCCESS( Status )) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LsaClose on Policy Handle returned 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectError;
+ }
+ }
+
+ return(BooleanStatus);
+
+SetQuerySecurityObjectError:
+
+ BooleanStatus = FALSE;
+
+ goto SetQuerySecurityObjectFinish;
+}
+
+
+NTSTATUS
+CtLsaSetQuerySecurityObjectSub(
+ IN LSA_HANDLE ObjectHandle,
+ IN PSID NewOwnerSid
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaSet/QuerySecurityObject API by attempting
+ to change the owner of the object and DACL in the SD.
+
+Arguments:
+
+ Objecthandle - Handle to an LSA Object.
+
+ NewOwnerSid - The SID of the new owner. Note that the assignment
+ of this Sid is expected to fail if it is not included in the
+ client's token or is the Sid of a group that is non-assignable
+ as owner.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ Replies back from called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ PSECURITY_DESCRIPTOR ExistingSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR UpdatedSecurityDescriptor = NULL;
+ SECURITY_DESCRIPTOR ModificationSecurityDescriptor;
+ SECURITY_INFORMATION QuerySecurityInformation;
+ SECURITY_INFORMATION SetSecurityInformation;
+ PACL Dacl = NULL;
+ ULONG DaclLength;
+ PSID UpdatedOwnerSid = NULL;
+ BOOLEAN UpdatedOwnerDefaulted;
+
+ //
+ // Query the existing Security on the object.
+ //
+
+ QuerySecurityInformation = DACL_SECURITY_INFORMATION |
+ OWNER_SECURITY_INFORMATION;
+
+ Status = LsaQuerySecurityObject(
+ ObjectHandle,
+ QuerySecurityInformation,
+ &ExistingSecurityDescriptor
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LsaQuerySecurityObject (existing security) returned 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ if (ExistingSecurityDescriptor == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LsaQuerySecurityObject (existing security) returned a NULL"
+ " Security Descriptor\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ //
+ // Now modify the Security on the object. Specifically, we will
+ // assign ownership to the designated NewOwnerSid and we will modify the
+ // DACL on the object to grant SECRET_WRITE access to that Sid.
+ //
+
+ Status = RtlCreateSecurityDescriptor(
+ &ModificationSecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "RtlCreateSecurityDescriptor (modification SD) returned a NULL"
+ " Security Descriptor\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ Status = RtlSetOwnerSecurityDescriptor (
+ &ModificationSecurityDescriptor,
+ NewOwnerSid,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "RtlSetOwnerSecurityDescriptor (modification SD) returned a NULL"
+ " Security Descriptor\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ //
+ // Calculate length of DACL required. It will hold one Access Allowed
+ // ACE for the Sid. The size of the DACL is the size of the ACL header
+ // plus the size of the ACE minus a redundant ULONG built into the header.
+ //
+
+ DaclLength = sizeof (ACL) - sizeof (ULONG) +
+ sizeof (ACCESS_ALLOWED_ACE ) +
+ RtlLengthSid( NewOwnerSid );
+
+ Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, DaclLength );
+
+ Status = RtlCreateAcl( Dacl, DaclLength, ACL_REVISION );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ //
+ // Now add the Access Allowed ACE for the group to the object's DACL.
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ Dacl,
+ ACL_REVISION,
+ SECRET_WRITE,
+ NewOwnerSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ //
+ // Hook the DACL to the Security Descriptor.
+ //
+
+ Status = RtlSetDaclSecurityDescriptor (
+ &ModificationSecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ SetSecurityInformation = DACL_SECURITY_INFORMATION |
+ OWNER_SECURITY_INFORMATION;
+
+ Status = LsaSetSecurityObject(
+ ObjectHandle,
+ SetSecurityInformation,
+ &ModificationSecurityDescriptor
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ //
+ // Query the updated Security on the object.
+ //
+
+ QuerySecurityInformation = DACL_SECURITY_INFORMATION |
+ OWNER_SECURITY_INFORMATION;
+
+ Status = LsaQuerySecurityObject(
+ ObjectHandle,
+ QuerySecurityInformation,
+ &UpdatedSecurityDescriptor
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LsaQuerySecurityObject (updated security) returned 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ if (UpdatedSecurityDescriptor == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LsaQuerySecurityObject (updated security) returned a NULL"
+ " Security Descriptor\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ //
+ // Verify that the updated owner returned matches that set.
+ //
+
+ UpdatedOwnerDefaulted = FALSE;
+
+ Status = RtlGetOwnerSecurityDescriptor(
+ UpdatedSecurityDescriptor,
+ &UpdatedOwnerSid,
+ &UpdatedOwnerDefaulted
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "RtlGetOwnerSecurityDescriptor (updated security) returned 0x%lx\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ if (UpdatedOwnerDefaulted) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "RtlGetOwnerSecurityDescriptor (updated security) returned\n"
+ "UpdatedOwnerDefaulted as TRUE\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ if (UpdatedOwnerSid == NULL) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "RtlGetOwnerSecurityDescriptor (updated security) returned\n"
+ " a NULL owner Sid\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ if (!RtlValidSid( UpdatedOwnerSid)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "RtlGetOwnerSecurityDescriptor (updated security) returned\n"
+ " an invalid owner Sid\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+ if (!RtlEqualSid( UpdatedOwnerSid, NewOwnerSid)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "Updated Owner Sid does not match NewOwnerSid\n",
+ Status
+ );
+
+ goto SetQuerySecurityObjectSubError;
+ }
+
+SetQuerySecurityObjectSubFinish:
+
+ //
+ // If necessary, free the Dacl.
+ //
+
+ if (Dacl != NULL) {
+
+ RtlFreeHeap( RtlProcessHeap(), 0, Dacl );
+ Dacl = NULL;
+ }
+
+ //
+ // If necessary, free the queried Existing Security Descriptor.
+ //
+
+ if ( ExistingSecurityDescriptor != NULL ) {
+
+ LsaFreeMemory( ExistingSecurityDescriptor );
+ ExistingSecurityDescriptor = NULL;
+ }
+
+ //
+ // If necessary, free the queried Updated Security Descriptor.
+ //
+
+ if ( UpdatedSecurityDescriptor != NULL ) {
+
+ LsaFreeMemory( UpdatedSecurityDescriptor );
+ UpdatedSecurityDescriptor = NULL;
+ }
+
+ return(Status);
+
+SetQuerySecurityObjectSubError:
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ goto SetQuerySecurityObjectSubFinish;
+}
+
+BOOLEAN
+CtLsaGeneralEnumeratePrivileges(
+ IN OPTIONAL PUNICODE_STRING WorkstationName
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the LsaEnumeratePrivileges API.
+
+Arguments:
+
+ WorkstationName - Specifies the name of the target Workstation whose
+ Policy Object wil be accessed.
+
+Return Values:
+
+ BOOLEAN - TRUE if successful, else FALSE
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ ULONG CountReturned;
+ ULONG TotalCountReturned;
+ ULONG EnumerationContext;
+ ULONG PreferedMaximumLength;
+ LSA_HANDLE PolicyHandle = NULL;
+ PPOLICY_PRIVILEGE_DEFINITION Privileges = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ ULONG EnumerationCount;
+ ULONG OriginalEnumerationContext;
+
+ DbgPrint("[2] - Test Enumerate Privileges API\n");
+
+ //
+ // First open a Handle to the LSA.
+ //
+
+ CtLsaInitObjectAttributes( &ObjectAttributes, &SecurityQualityOfService );
+
+ Status = LsaOpenPolicy(
+ WorkstationName,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION | POLICY_CREATE_SECRET,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Set/Query Security Object Test failed\n"
+ "LsaOpenPolicy returned 0x%lx\n",
+ Status
+ );
+
+ goto EnumeratePrivilegesError;
+ }
+
+ EnumerationCount = 0;
+ EnumerationContext = 0;
+ TotalCountReturned = 0;
+ CountReturned = 0x0fffffffL;
+ PreferedMaximumLength = 1;
+
+ while (NT_SUCCESS(Status)) {
+
+ CountReturned = 0x0fffffffL;
+
+ OriginalEnumerationContext = EnumerationContext;
+
+ Status = LsaEnumeratePrivileges(
+ PolicyHandle,
+ &EnumerationContext,
+ &Privileges,
+ PreferedMaximumLength,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (OriginalEnumerationContext <
+ SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE + 1) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumerate Privileges Test failed\n"
+ "LsaEnumeratePrivileges returned 0x%lx\n",
+ Status
+ );
+
+ BooleanStatus = FALSE;
+
+ } else {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumerate Privileges Test failed\n"
+ "LsaEnumeratePrivileges returned 0x%lx\n"
+ ".. expected 0x%lx (STATUS_NO_MORE_ENTRIES)\n",
+ STATUS_NO_MORE_ENTRIES,
+ Status
+ );
+
+ BooleanStatus = FALSE;
+ }
+
+ }
+
+ break;
+ }
+
+ //
+ // A success status was returned. Only STATUS_SUCCESS or
+ // STATUS_MORE_ENTRIES are allowed.
+ //
+
+ if ((Status != STATUS_SUCCESS) && (Status != STATUS_MORE_ENTRIES)) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumerate Privileges Test failed\n"
+ "LsaEnumeratePrivileges returned 0x%lx\n",
+ Status
+ );
+
+ break;
+ }
+
+ //
+ // Check EnumerationContext and CountReturned.
+ //
+
+ TotalCountReturned += CountReturned;
+
+ if (EnumerationContext != TotalCountReturned) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumerate Privileges Test failed\n"
+ "EnumerationContext does not match TotalCountReturned\n"
+ "EnumerationContext = %d, TotalCountReturned = %d\n",
+ EnumerationContext,
+ TotalCountReturned
+ );
+
+ goto EnumeratePrivilegesError;
+ }
+
+ EnumerationCount++;
+ }
+
+ if (EnumerationCount == 0) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumerate Privileges Test failed\n"
+ "No enumerations took place\n"
+ );
+
+ goto EnumeratePrivilegesError;
+ }
+
+EnumeratePrivilegesFinish:
+
+ //
+ // If necessary, free the memory allocated for the Privileges
+ //
+
+ if (Privileges == NULL) {
+
+ Status = LsaFreeMemory( Privileges );
+
+ Privileges = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumerate Privileges Test failed\n"
+ "LsaFreeMemory for Privileges buffer returned 0x%lx\n",
+ Status
+ );
+
+ goto EnumeratePrivilegesError;
+ }
+ }
+
+ //
+ // If necessary, close the handle to the Policy Object.
+ //
+
+ if (PolicyHandle != NULL) {
+
+ Status = LsaClose( PolicyHandle );
+ PolicyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "LSA RPC CT - Enumerate Privileges Test failed\n"
+ "LsaClose on Policy Handle returned 0x%lx\n",
+ Status
+ );
+
+ goto EnumeratePrivilegesError;
+ }
+ }
+
+ return(BooleanStatus);
+
+EnumeratePrivilegesError:
+
+ BooleanStatus = FALSE;
+ goto EnumeratePrivilegesFinish;
+}
+
+
+BOOLEAN
+CtLsaGeneralInitUnknownSid(
+ IN PSID Sid,
+ OUT PCT_UNKNOWN_SID_ENTRY UnknownSidInfo,
+ IN BOOLEAN DomainKnown
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes information about an Unknown Sid.
+
+Arguments:
+
+ Sid - Pointer to the unknown Sid.
+
+ UnknownSidInfo - Pointer to entry for unknown Sid info to be filled in.
+
+ DomainKnown - TRUE if the domain is known, else FALSE.
+
+Return Values:
+
+ BOOLEAN - TRUE if successful, else FALSE.
+
+--*/
+
+{
+ ULONG LengthRequiredDomainSid;
+ UCHAR SubAuthorityCountSid;
+ NTSTATUS Status;
+
+ UnknownSidInfo->Sid = Sid;
+
+ //
+ // If the domain is known, store its Sid, and, for the name of the
+ // Sid, store the RelativeId converted to Unicode.
+ //
+
+ if (DomainKnown) {
+
+ UnknownSidInfo->DomainKnown = TRUE;
+
+ //
+ // Construct the Domain Sid by making a copy of the
+ // Sid and decrementing its SubAuthorityCount. Store
+ // pointer to the Domain Sid in the Trust Information.
+ //
+
+ SubAuthorityCountSid = *(RtlSubAuthorityCountSid(Sid));
+
+ LengthRequiredDomainSid = RtlLengthRequiredSid( SubAuthorityCountSid-1);
+
+ UnknownSidInfo->Sid = TstAllocatePool(PagedPool, LengthRequiredDomainSid);
+
+ if (UnknownSidInfo->Sid == NULL) {
+
+ return(FALSE);
+ }
+
+ RtlMoveMemory(
+ UnknownSidInfo->Sid,
+ Sid,
+ LengthRequiredDomainSid
+ );
+
+ (*(RtlSubAuthorityCountSid(Sid)))--;
+
+ //
+ // Convert the Relative Id to a Unicode Name and store in
+ // the Translation.
+ //
+
+ Status = CtLsaGeneralSidToLogicalNameObject(
+ Sid,
+ &UnknownSidInfo->Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("LSA RPC CT - Lookup Sids - failed to allocate memory\n");
+ DbgPrint("for unknown Sid's Relative id in Unicode Form\n");
+
+ return(FALSE);
+ }
+
+ } else {
+
+ //
+ // The Domain is not expected to be known. In this case, the
+ // Domain Sid and name are undefined and the whole Sid is
+ // converted to character form.
+ //
+
+ UnknownSidInfo->DomainKnown = FALSE;
+
+ Status = CtRtlConvertSidToUnicodeString(
+ Sid,
+ &UnknownSidInfo->Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(FALSE);
+ }
+ }
+
+ return(TRUE);
+}
+
+
+BOOLEAN
+CtEqualQuotaLimits(
+ IN PQUOTA_LIMITS QuotaLimits1,
+ IN PQUOTA_LIMITS QuotaLimits2
+ )
+
+/*++
+
+Routine Description:
+
+ This function compares two sets of quota limits to see if they are
+ equal. For each mismatch found a diagnostic message is printed.
+
+Arguments:
+
+ QuotaLimits1 - Pointer to first comparand.
+
+ QuotaLimits2 - Pointer to second comparand.
+
+Return Value:
+
+ BOOLEAN - TRUE if all fields match, else FALSE.
+
+--*/
+
+{
+ BOOLEAN EqualStatus = TRUE;
+
+ //
+ // Compare the quota limits with those set
+ //
+
+ if (QuotaLimits1->PagedPoolLimit !=
+ QuotaLimits2->PagedPoolLimit) {
+
+ DbgPrint("Quota Limit Paged Pool mismatch\n");
+ DbgPrint(
+ "Set 0x%lx, Returned 0x%lx\n",
+ (QuotaLimits2->PagedPoolLimit),
+ (QuotaLimits1->PagedPoolLimit)
+ );
+ EqualStatus = FALSE;
+ }
+
+ if (QuotaLimits1->NonPagedPoolLimit !=
+ QuotaLimits2->NonPagedPoolLimit) {
+
+ DbgPrint("Quota Limit Non-Paged Pool mismatch\n");
+ DbgPrint(
+ "Set 0x%lx, Returned 0x%lx\n",
+ (QuotaLimits2->NonPagedPoolLimit),
+ (QuotaLimits1->NonPagedPoolLimit)
+ );
+ EqualStatus = FALSE;
+ }
+
+ if (QuotaLimits1->MinimumWorkingSetSize !=
+ QuotaLimits2->MinimumWorkingSetSize) {
+
+ DbgPrint("Quota Limit Min Working SetSize mismatch\n");
+ DbgPrint(
+ "Set 0x%lx, Returned 0x%lx\n",
+ (QuotaLimits2->MinimumWorkingSetSize),
+ (QuotaLimits1->MinimumWorkingSetSize)
+ );
+ EqualStatus = FALSE;
+ }
+
+ if (QuotaLimits1->MaximumWorkingSetSize !=
+ QuotaLimits2->MaximumWorkingSetSize) {
+
+ DbgPrint("Quota Limit Max Working Set Size mismatch\n");
+ DbgPrint(
+ "Set 0x%lx, Returned 0x%lx\n",
+ (QuotaLimits2->MaximumWorkingSetSize),
+ (QuotaLimits1->MaximumWorkingSetSize)
+ );
+ EqualStatus = FALSE;
+ }
+
+ if (QuotaLimits1->PagefileLimit !=
+ QuotaLimits2->PagefileLimit) {
+
+ DbgPrint("Quota Limit Pagefile Limit mismatch\n");
+ DbgPrint(
+ "Set 0x%lx, Returned 0x%lx\n",
+ (QuotaLimits2->PagefileLimit),
+ (QuotaLimits1->PagefileLimit)
+ );
+ EqualStatus = FALSE;
+ }
+
+ if (QuotaLimits1->TimeLimit.LowPart !=
+ QuotaLimits2->TimeLimit.LowPart) {
+
+ DbgPrint("Quota Limit Time Limit mismatch\n");
+ DbgPrint(
+ "Set 0x%lx, Returned 0x%lx\n",
+ (QuotaLimits2->TimeLimit.LowPart),
+ (QuotaLimits1->TimeLimit.LowPart)
+ );
+ EqualStatus = FALSE;
+ }
+
+ return EqualStatus;
+}
+
+
+VOID
+CtLsaInitObjectAttributes(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the given Object Attributes structure, including
+ Security Quality Of Service. Memory must be allcated for both
+ ObjectAttributes and Security QOS by the caller.
+
+Arguments:
+
+ ObjectAttributes - Pointer to Object Attributes to be initialized.
+
+ SecurityQualityOfService - Pointer to Security QOS to be initialized.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SecurityQualityOfService->Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ SecurityQualityOfService->ImpersonationLevel = SecurityImpersonation;
+ SecurityQualityOfService->ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQualityOfService->EffectiveOnly = FALSE;
+
+ //
+ // Set up the object attributes prior to opening the LSA.
+ //
+
+ InitializeObjectAttributes(
+ ObjectAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL
+ );
+
+ //
+ // The InitializeObjectAttributes macro presently stores NULL for
+ // the SecurityQualityOfService field, so we must manually copy that
+ // structure for now.
+ //
+
+ ObjectAttributes->SecurityQualityOfService = SecurityQualityOfService;
+}
+
+
+BOOLEAN
+CtLsaVariableInitialization()
+
+/*++
+
+Routine Description:
+
+ This function initializes the global variables specific to the security
+ test environment.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if variables successfully initialized.
+ FALSE if not successfully initialized.
+
+--*/
+
+{
+ ULONG SidWithZeroSubAuthorities;
+ ULONG SidWithOneSubAuthority;
+ ULONG SidWithThreeSubAuthorities;
+ ULONG SidWithFourSubAuthorities;
+
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+
+ SID_IDENTIFIER_AUTHORITY BedrockAuthority = BEDROCK_AUTHORITY;
+
+ SID_IDENTIFIER_AUTHORITY BedrockAAuthority = BEDROCKA_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockBAuthority = BEDROCKB_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockCAuthority = BEDROCKC_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockDAuthority = BEDROCKD_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockEAuthority = BEDROCKE_AUTHORITY;
+
+ //
+ // The following SID sizes need to be allocated
+ //
+
+ SidWithZeroSubAuthorities = RtlLengthRequiredSid( 0 );
+ SidWithOneSubAuthority = RtlLengthRequiredSid( 1 );
+ SidWithThreeSubAuthorities = RtlLengthRequiredSid( 3 );
+ SidWithFourSubAuthorities = RtlLengthRequiredSid( 4 );
+
+ //
+ // Allocate SIDs specific to the Component Tests. These relate to the
+ // Bedrock domains.
+ //
+
+ BedrockDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockADomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockBDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockCDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockDDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockEDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+
+ FredSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ WilmaSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ PebblesSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ DinoSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ BarneySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ BettySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ BambamSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ FlintstoneSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ RubbleSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ AdultSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ ChildSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ NeandertholSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ RtlInitializeSid( BedrockDomainSid, &BedrockAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockDomainSid, 0)) = BEDROCK_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockDomainSid, 1)) = BEDROCK_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockDomainSid, 2)) = BEDROCK_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockADomainSid, &BedrockAAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockADomainSid, 0)) = BEDROCKA_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockADomainSid, 1)) = BEDROCKA_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockADomainSid, 2)) = BEDROCKA_SUBAUTHORITY_2;
+
+
+ RtlInitializeSid( BedrockBDomainSid, &BedrockBAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockBDomainSid, 0)) = BEDROCKB_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockBDomainSid, 1)) = BEDROCKB_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockBDomainSid, 2)) = BEDROCKB_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockCDomainSid, &BedrockCAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockCDomainSid, 0)) = BEDROCKC_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockCDomainSid, 1)) = BEDROCKC_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockCDomainSid, 2)) = BEDROCKC_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockDDomainSid, &BedrockDAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockDDomainSid, 0)) = BEDROCKD_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockDDomainSid, 1)) = BEDROCKD_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockDDomainSid, 2)) = BEDROCKD_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockEDomainSid, &BedrockEAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockEDomainSid, 0)) = BEDROCKE_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockEDomainSid, 1)) = BEDROCKE_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockEDomainSid, 2)) = BEDROCKE_SUBAUTHORITY_2;
+
+ RtlCopySid( SidWithFourSubAuthorities, FredSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( FredSid )) += 1;
+ *(RtlSubAuthoritySid( FredSid, 3)) = FRED_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, WilmaSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( WilmaSid )) += 1;
+ *(RtlSubAuthoritySid( WilmaSid, 3)) = WILMA_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, PebblesSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( PebblesSid )) += 1;
+ *(RtlSubAuthoritySid( PebblesSid, 3)) = PEBBLES_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, DinoSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( DinoSid )) += 1;
+ *(RtlSubAuthoritySid( DinoSid, 3)) = DINO_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BarneySid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BarneySid )) += 1;
+ *(RtlSubAuthoritySid( BarneySid, 3)) = BARNEY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BettySid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BettySid )) += 1;
+ *(RtlSubAuthoritySid( BettySid, 3)) = BETTY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BambamSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BambamSid )) += 1;
+ *(RtlSubAuthoritySid( BambamSid, 3)) = BAMBAM_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, FlintstoneSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( FlintstoneSid )) += 1;
+ *(RtlSubAuthoritySid( FlintstoneSid, 3)) = FLINTSTONE_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, RubbleSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( RubbleSid )) += 1;
+ *(RtlSubAuthoritySid( RubbleSid, 3)) = RUBBLE_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, AdultSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( AdultSid )) += 1;
+ *(RtlSubAuthoritySid( AdultSid, 3)) = ADULT_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, ChildSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( ChildSid )) += 1;
+ *(RtlSubAuthoritySid( ChildSid, 3)) = CHILD_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, NeandertholSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( NeandertholSid )) += 1;
+ *(RtlSubAuthoritySid( NeandertholSid, 3)) = NEANDERTHOL_RID;
+
+ //
+ // Initialize the names of the Bedrock Domain inhabitants.
+ //
+
+
+ RtlInitUnicodeString( &BedrockDomainName, L"Bedrock");
+
+ RtlInitUnicodeString( &BedrockADomainName, L"BedrockA");
+ RtlInitUnicodeString( &BedrockBDomainName, L"BedrockB");
+ RtlInitUnicodeString( &BedrockCDomainName, L"BedrockC");
+ RtlInitUnicodeString( &BedrockDDomainName, L"BedrockD");
+ RtlInitUnicodeString( &BedrockEDomainName, L"BedrockE");
+
+ RtlInitUnicodeString( &FredName, L"Fred");
+ RtlInitUnicodeString( &WilmaName, L"Wilma");
+ RtlInitUnicodeString( &PebblesName, L"Pebbles");
+ RtlInitUnicodeString( &DinoName, L"Dino");
+
+ RtlInitUnicodeString( &BarneyName, L"Barney");
+ RtlInitUnicodeString( &BettyName, L"Betty");
+ RtlInitUnicodeString( &BambamName, L"Bambam");
+
+ RtlInitUnicodeString( &FlintstoneName, L"Flintstone");
+ RtlInitUnicodeString( &RubbleName, L"Rubble");
+
+ RtlInitUnicodeString( &AdultName, L"Adult");
+ RtlInitUnicodeString( &ChildName, L"Child");
+
+ RtlInitUnicodeString( &NeandertholName, L"Neanderthol");
+
+ return TRUE;
+}
+
+
+NTSTATUS
+CtLsaGeneralSidToLogicalNameObject(
+ IN PSID Sid,
+ OUT PUNICODE_STRING LogicalName
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates the Logical Name (Internal LSA Database Name)
+ of an object from its Sid. Currently, only the Relative Id (lowest
+ sub-authority) is used due to Registry and hence Lsa Database limits
+ on name components to 8 characters. The Rid is extracted and converted
+ to an 8-digit Unicode Integer.
+
+Arguments:
+
+ Sid - Pointer to the Sid to be looked up. It is the caller's
+ responsibility to ensure that the Sid has valid syntax.
+
+ LogicalName - Pointer to a Unicode String structure that will receive
+ the Logical Name. Note that memory for the string buffer in this
+ Unicode String will be allocated by this routine if successful. The
+ caller must free this memory after use by calling RtlFreeUnicodeString.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Status code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to allocate buffer for Unicode String name.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG Rid;
+ UCHAR SubAuthorityCount;
+ UCHAR RidNameBufferAnsi[9];
+
+ ANSI_STRING CharacterSidAnsi;
+
+ //
+ // First, verify that the given Sid is valid
+ //
+
+ if (!RtlValidSid( Sid )) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Sid is valid. If however, the SubAuthorityCount is zero,
+ // we cannot have a Rid so return error.
+ //
+
+ SubAuthorityCount = ((PLSAPR_SID) Sid)->SubAuthorityCount;
+
+ if (SubAuthorityCount == 0) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Sid has at least one subauthority. Get the lowest subauthority
+ // (i.e. the Rid).
+ //
+
+ Rid = ((PLSAPR_SID) Sid)->SubAuthority[SubAuthorityCount - 1];
+
+ //
+ // Now convert the Rid to an 8-digit numeric character string
+ //
+
+ Status = RtlIntegerToChar( Rid, 16, -8, RidNameBufferAnsi );
+
+ //
+ // Need to add null terminator to string
+ //
+
+ RidNameBufferAnsi[8] = 0;
+
+ //
+ // Initialize an ANSI string structure with the converted name.
+ //
+
+ RtlInitString( &CharacterSidAnsi, RidNameBufferAnsi );
+
+ //
+ // Convert the ANSI string structure to Unicode form
+ //
+
+ Status = RtlAnsiStringToUnicodeString(
+ LogicalName,
+ &CharacterSidAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return Status;
+}
+
+
+BOOLEAN
+CtLsaGeneralVerifyTrustInfo(
+ IN PLSA_TRUST_INFORMATION TrustInformation,
+ IN PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSidEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that Trust Information given for a Well Known
+ Sid or Name is correct.
+
+Parameters:
+
+ TrustInformation - Pointer to Trust Information.
+
+ WellKnownSidEntry - Pointer to entry in the table of Well Known Sids.
+
+Return Values:
+
+ BOOLEAN - TRUE if Trust Information is correct, else FALSE.
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ LSA_TRUST_INFORMATION ExpectedTrustInformation;
+
+ ExpectedTrustInformation.Name = WellKnownSidEntry->DomainName;
+
+ //
+ // Compare the Domain Names (or descriptive text)
+ //
+
+ if ((ExpectedTrustInformation.Name.Buffer != NULL) &&
+ (ExpectedTrustInformation.Name.Length != 0)) {
+
+ if (TrustInformation->Name.Buffer == NULL) {
+
+ DbgPrint(".. NULL Domain Name Unicode Buffer returned\n");
+ BooleanStatus = FALSE;
+
+ } else if (!RtlEqualUnicodeString(
+ &TrustInformation->Name,
+ &ExpectedTrustInformation.Name,
+ FALSE
+ )) {
+
+ DbgPrint(".. mismatch on Domain Name or text\n");
+ BooleanStatus = FALSE;
+ }
+ }
+
+ //
+ // Compare the Domain Sids in the Trust Information. First
+ // we need to derive the expected Domain or Prefix Sid by
+ // making a copy of the original Sid and decrementing the
+ // SubAuthorityCount (non-Domain Sids) or just copying the
+ // Sid (domain sids).
+ //
+
+ if (WellKnownSidEntry->Use == SidTypeDomain) {
+
+ ExpectedTrustInformation.Sid = WellKnownSidEntry->Sid;
+
+ } else {
+
+ ExpectedTrustInformation.Sid =
+ TstAllocatePool(
+ PagedPool,
+ RtlLengthSid(WellKnownSidEntry->Sid)
+ );
+
+ if (ExpectedTrustInformation.Sid == NULL) {
+
+ DbgPrint("LSA RPC CT - Failed to allocate memory\n");
+ return(FALSE);
+ }
+
+ //
+ // Copy the Sid and decrement the SubAuthorityCount..
+ //
+
+ RtlMoveMemory(
+ ExpectedTrustInformation.Sid,
+ WellKnownSidEntry->Sid,
+ RtlLengthSid(WellKnownSidEntry->Sid)
+ );
+
+ (*RtlSubAuthorityCountSid( ExpectedTrustInformation.Sid ))--;
+ }
+
+ //
+ // Now compare the Sid returned in the TrustInformation.
+ //
+
+ if (!RtlEqualSid(
+ TrustInformation->Sid,
+ ExpectedTrustInformation.Sid
+ )) {
+
+ DbgPrint(".. mismatch on Sid or text\n");
+
+ BooleanStatus = FALSE;
+ }
+
+ //
+ // If necessary, free the memory allocated for the Domain Sid.
+ //
+
+ if (WellKnownSidEntry->Use != SidTypeDomain) {
+
+ if (ExpectedTrustInformation.Sid != NULL) {
+
+ TstDeallocatePool( ExpectedTrustInformation.Sid );
+ }
+ }
+
+ return(BooleanStatus);
+}
+
+
+BOOLEAN
+CtLsaGeneralBuildSid(
+ PSID *Sid,
+ PSID DomainSid,
+ ULONG RelativeId
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a Sid from a Domain Sid and a RelativeId.
+
+Arguments:
+
+ Sid - Receives a pointer to the constructed Sid.
+
+ DomainSid - Points to a Domain Sid
+
+ RelativeId - Contains a Relative Id.
+
+Return Values:
+
+ BOOLEAN - TRUE if successful, else FALSE.
+
+--*/
+
+{
+ PSID OutputSid = NULL;
+ ULONG OutputSidLength;
+ UCHAR SubAuthorityCount;
+
+ SubAuthorityCount = *RtlSubAuthorityCountSid( DomainSid ) + (UCHAR) 1;
+ OutputSidLength = RtlLengthRequiredSid( SubAuthorityCount );
+
+ OutputSid = LocalAlloc( (UINT) LMEM_FIXED, (UINT) OutputSidLength );
+
+ if (OutputSid == NULL) {
+
+ return(FALSE);
+ }
+
+ RtlMoveMemory( OutputSid, DomainSid, OutputSidLength - sizeof (ULONG));
+ (*RtlSubAuthorityCountSid( OutputSid ))++;
+ (*RtlSubAuthoritySid( OutputSid, SubAuthorityCount - (UCHAR) 1)) = RelativeId;
+
+ *Sid = OutputSid;
+ return(TRUE);
+}
+
+
+NTSTATUS
+CtRtlConvertSidToUnicodeString(
+ IN PSID Sid,
+ OUT PUNICODE_STRING UnicodeString
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a Sid to Text format.
+
+Arguments:
+
+ Sid - Pointer to Sid.
+
+ UnicodeString - Pointer to Output Unicode String.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory, to complete the call.
+
+WARNING! This is a temporary hack.
+
+--*/
+
+{
+ NTSTATUS Status;
+ UCHAR Buffer[256];
+ UCHAR String[256];
+
+ UCHAR i;
+ ULONG Tmp;
+
+ PISID iSid = (PISID)Sid; // pointer to opaque structure
+
+ ANSI_STRING AnsiString;
+
+ sprintf(Buffer, "S-%u-", (USHORT)iSid->Revision );
+ lstrcpyA((LPSTR) String, (LPCSTR) Buffer);
+
+ if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
+ (iSid->IdentifierAuthority.Value[1] != 0) ){
+ sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)iSid->IdentifierAuthority.Value[0],
+ (USHORT)iSid->IdentifierAuthority.Value[1],
+ (USHORT)iSid->IdentifierAuthority.Value[2],
+ (USHORT)iSid->IdentifierAuthority.Value[3],
+ (USHORT)iSid->IdentifierAuthority.Value[4],
+ (USHORT)iSid->IdentifierAuthority.Value[5] );
+ lstrcatA((LPSTR) String, (LPCSTR) Buffer);
+
+ } else {
+
+ Tmp = (ULONG)iSid->IdentifierAuthority.Value[5] +
+ (ULONG)(iSid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(iSid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
+ sprintf(Buffer, "%lu", Tmp);
+ lstrcatA((LPSTR) String, (LPCSTR) Buffer);
+ }
+
+ for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
+ sprintf(Buffer, "-%lu", iSid->SubAuthority[i]);
+ lstrcatA((LPSTR) String, (LPCSTR) Buffer);
+ }
+
+ //
+ // Convert the string to a Unicode String
+ //
+
+ RtlInitString(&AnsiString, (PSZ) String);
+
+ Status = RtlAnsiStringToUnicodeString( UnicodeString, &AnsiString, TRUE);
+
+ return(Status);
+}
+
+
+VOID
+lsassmain ()
+{
+ //
+ // Do the following:
+ //
+ // Initialize the RPC server
+ //
+ // Initialize LSA (this starts the RPC server)
+ //
+ // Pause for installation if necessary
+ //
+ // Initialize SAM
+ //
+ // Initialize the service-controller service
+ // dispatcher.
+ //
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Initialize the LSA.
+ // If unsuccessful, we must exit with status so that the SM knows
+ // something has gone wrong.
+ //
+
+ Status = LsapInitLsa();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // Initialize SAM
+ //
+
+ Status = SamIInitialize();
+
+ //
+ // Initialize the service dispatcher.
+ //
+ // Note : we initialize the service dispatcher even when the sam
+ // service is not started, this will make the service controller
+ // starts successfully when the netlogon service is installed.
+ //
+
+
+ Status = ServiceInit();
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ DbgPrint("ctlsarpc - LSA initialized, set Ct breakpoints now\n");
+ DbgBreakPoint();
+
+// NtTerminateThread( NtCurrentThread(), Status );
+}
diff --git a/private/lsa/server/db.h b/private/lsa/server/db.h
new file mode 100644
index 000000000..60c0f17bf
--- /dev/null
+++ b/private/lsa/server/db.h
@@ -0,0 +1,983 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ db.h
+
+Abstract:
+
+ LSA Database Exported Function Definitions, Datatypes and Defines
+
+ This module contains the LSA Database Routines that may be called
+ by parts of the LSA outside the Database sub-component.
+
+Author:
+
+ Scott Birrell (ScottBi) August 26, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LSA_DB_
+#define _LSA_DB_
+
+//
+// Maximum Number of attributes in the various object types
+//
+
+#define LSAP_DB_ATTRS_POLICY ((ULONG) 0x00000010L)
+#define LSAP_DB_ATTRS_ACCOUNT ((ULONG) 0x00000010L)
+#define LSAP_DB_ATTRS_DOMAIN ((ULONG) 0x00000010L)
+#define LSAP_DB_ATTRS_SECRET ((ULONG) 0x00000010L)
+
+//
+// Constants for matching options on Sid/Name lookup operations
+//
+
+#define LSAP_DB_MATCH_ON_SID ((ULONG) 0x00000001L)
+#define LSAP_DB_MATCH_ON_NAME ((ULONG) 0x00000002L)
+
+//
+// Options for LsapDbLookupSidsInLocalDomains()
+//
+
+#define LSAP_DB_SEARCH_BUILT_IN_DOMAIN ((ULONG) 0x00000001L)
+#define LSAP_DB_SEARCH_ACCOUNT_DOMAIN ((ULONG) 0x00000002L)
+
+//
+// Options for LsapDbMergeDisjointReferencedDomains
+//
+
+#define LSAP_DB_USE_FIRST_MERGAND_GRAPH ((ULONG) 0x00000001L)
+#define LSAP_DB_USE_SECOND_MERGAND_GRAPH ((ULONG) 0x00000002L)
+
+//
+// Option for updating Policy Database
+//
+
+#define LSAP_DB_UPDATE_POLICY_DATABASE ((ULONG) 0x00000001L)
+//
+// Option for updating Policy Database
+//
+
+#define LSAP_DB_UPDATE_POLICY_DATABASE ((ULONG) 0x00000001L)
+//
+// Maximum number of attributes corresponding to a Policy Object
+// Information Class
+//
+
+#define LSAP_DB_ATTRS_INFO_CLASS_POLICY ((ULONG) 0x00000005L)
+
+//
+// Maximum number of attributes corresponding to a Trusted Domain Object
+// Information Class
+//
+
+#define LSAP_DB_ATTRS_INFO_CLASS_DOMAIN ((ULONG) 0x00000002L)
+
+
+
+//
+// Global variables
+//
+
+extern BOOLEAN LsapDbRequiresSidInfo[];
+extern BOOLEAN LsapDbRequiresNameInfo[];
+extern LSAPR_HANDLE LsapDbHandle;
+extern BOOLEAN LsapSetupWasRun;
+extern BOOLEAN LsapDatabaseSetupPerformed;
+extern NT_PRODUCT_TYPE LsapProductType;
+
+
+//
+// Table of accesses required to query Policy Information. This table
+// is indexed by Policy Information Class
+//
+
+extern ACCESS_MASK LsapDbRequiredAccessQueryPolicy[];
+
+//
+// Table of accesses required to set Policy Information. This table
+// is indexed by Policy Information Class
+//
+
+extern ACCESS_MASK LsapDbRequiredAccessSetPolicy[];
+
+//
+// Table of accesses required to query TrustedDomain Information. This table
+// is indexed by TrustedDomain Information Class
+//
+
+extern ACCESS_MASK LsapDbRequiredAccessQueryTrustedDomain[];
+
+//
+// Table of accesses required to set TrustedDomain Information. This table
+// is indexed by TrustedDomain Information Class
+//
+
+extern ACCESS_MASK LsapDbRequiredAccessSetTrustedDomain[];
+
+//
+// Maximum Handle Reference Count
+//
+
+#define LSAP_DB_MAXIMUM_REFERENCE_COUNT ((ULONG) 0x00001000L)
+
+//
+// Default Computer Name used for Policy Account Domain Info
+//
+
+#define LSAP_DB_DEFAULT_COMPUTER_NAME (L"MACHINENAME")
+
+//
+// Options for the LsaDbReferenceObject and LsaDbDereferenceObject
+//
+
+#define LSAP_DB_ACQUIRE_LOCK ((ULONG) 0x00000001L)
+#define LSAP_DB_RELEASE_LOCK ((ULONG) 0x00000002L)
+#define LSAP_DB_NO_LOCK ((ULONG) 0x00000004L)
+#define LSAP_DB_START_TRANSACTION ((ULONG) 0x00000008L)
+#define LSAP_DB_FINISH_TRANSACTION ((ULONG) 0x00000010L)
+#define LSAP_DB_VALIDATE_HANDLE ((ULONG) 0x00000020L)
+#define LSAP_DB_TRUSTED ((ULONG) 0x00000040L)
+#define LSAP_DB_NOT_TRUSTED ((ULONG) 0x00000080L)
+#define LSAP_DB_DEREFERENCE_CONTR ((ULONG) 0x00000100L)
+#define LSAP_DB_ENABLE_NON_TRUSTED_ACCESS ((ULONG) 0x00000200L)
+#define LSAP_DB_DISABLE_NON_TRUSTED_ACCESS ((ULONG) 0x00000400L)
+#define LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK ((ULONG) 0x00000800L)
+#define LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK ((ULONG) 0x00001000L)
+#define LSAP_DB_RELEASE_LOG_QUEUE_LOCK ((ULONG) 0x00002000L)
+#define LSAP_DB_OMIT_REPLICATOR_NOTIFICATION ((ULONG) 0x00004000L)
+#define LSAP_DB_FREE_HANDLE ((ULONG) 0x00008000L)
+#define LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES ((ULONG) 0x00010000L)
+#define LSAP_DB_REBUILD_CACHE ((ULONG) 0x00020000L)
+#define LSAP_DB_PROMOTION_INCREMENT ((ULONG) 0x00040000L)
+
+#define LSAP_DB_STATE_MASK \
+ (LSAP_DB_ACQUIRE_LOCK | LSAP_DB_RELEASE_LOCK | LSAP_DB_NO_LOCK | \
+ LSAP_DB_START_TRANSACTION | LSAP_DB_FINISH_TRANSACTION | \
+ LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK | LSAP_DB_RELEASE_LOG_QUEUE_LOCK)
+
+//
+// Configuration Registry Root Key for Lsa Database. All Physical Object
+// and Attribute Names are relative to this Key.
+//
+
+#define LSAP_DB_ROOT_REG_KEY_NAME L"\\Registry\\Machine\\Security"
+
+//
+// LSA Database Object Defines
+//
+
+#define LSAP_DB_OBJECT_OPEN FILE_OPEN
+#define LSAP_DB_OBJECT_OPEN_IF FILE_OPEN_IF
+#define LSAP_DB_OBJECT_CREATE FILE_CREATE
+#define LSAP_DB_KEY_VALUE_MAX_LENGTH (0x00000040L)
+#define LSAP_DB_LOGICAL_NAME_MAX_LENGTH (0x00000100L)
+
+//
+// LSA Database Object SubKey Defines
+//
+
+#define LSAP_DB_SUBKEY_OPEN FILE_OPEN
+#define LSAP_DB_SUBKEY_OPEN_IF FILE_OPEN_IF
+#define LSAP_DB_SUBKEY_CREATE FILE_CREATE
+
+//
+// Growth Delta for Referenced Domain Lists
+//
+
+#define LSAP_DB_REF_DOMAIN_DELTA ((ULONG) 0x00000020L )
+
+//
+// The following data type is used in name and SID lookup services to
+// describe the domains referenced in the lookup operation.
+//
+// WARNING! This is an internal version of LSA_REFERENCED_DOMAIN_LIST
+// in ntlsa.h. It has an additional field, MaxEntries.
+//
+
+typedef struct _LSAP_DB_REFERENCED_DOMAIN_LIST {
+
+ ULONG Entries;
+ PLSA_TRUST_INFORMATION Domains;
+ ULONG MaxEntries;
+
+} LSAP_DB_REFERENCED_DOMAIN_LIST, *PLSAP_DB_REFERENCED_DOMAIN_LIST;
+
+// where members have the following usage:
+//
+// Entries - Is a count of the number of domains described in the
+// Domains array.
+//
+// Domains - Is a pointer to an array of Entries LSA_TRUST_INFORMATION data
+// structures.
+//
+// MaxEntries - Is the maximum number of entries that can be stored
+// in the current array
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// LSA Database Object Types
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// Lsa Database Object Type
+//
+
+typedef enum _LSAP_DB_OBJECT_TYPE_ID {
+
+ NullObject = 0,
+ PolicyObject,
+ TrustedDomainObject,
+ AccountObject,
+ SecretObject,
+ DummyLastObject
+
+} LSAP_DB_OBJECT_TYPE_ID, *PLSAP_DB_OBJECT_TYPE_ID;
+
+//
+// LSA Database Object Handle structure (Internal definition of LSAPR_HANDLE)
+//
+// Note that the Handle structure is public to clients of the Lsa Database
+// exported functions, e.g server API workers) so that they can get at things
+// like GrantedAccess.
+//
+
+typedef struct _LSAP_DB_HANDLE {
+
+ struct _LSAP_DB_HANDLE *Next;
+ struct _LSAP_DB_HANDLE *Previous;
+ BOOLEAN Allocated;
+ ULONG ReferenceCount;
+ UNICODE_STRING LogicalNameU;
+ UNICODE_STRING PhysicalNameU;
+ PSID Sid;
+ HANDLE KeyHandle;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+ struct _LSAP_DB_HANDLE *ContainerHandle;
+ ACCESS_MASK DesiredAccess;
+ ACCESS_MASK GrantedAccess;
+ BOOLEAN GenerateOnClose;
+ BOOLEAN Trusted;
+ BOOLEAN DeletedObject;
+ ULONG Options;
+
+} *LSAP_DB_HANDLE, **PLSAP_DB_HANDLE;
+
+//
+// LSA Database Object Sid Enumeration Buffer
+//
+
+typedef struct _LSAP_DB_SID_ENUMERATION_BUFFER {
+
+ ULONG EntriesRead;
+ PSID *Sids;
+
+} LSAP_DB_SID_ENUMERATION_BUFFER, *PLSAP_DB_SID_ENUMERATION_BUFFER;
+
+//
+// LSA Database Object Name Enumeration Buffer
+//
+
+typedef struct _LSAP_DB_NAME_ENUMERATION_BUFFER {
+
+ ULONG EntriesRead;
+ PUNICODE_STRING Names;
+
+} LSAP_DB_NAME_ENUMERATION_BUFFER, *PLSAP_DB_NAME_ENUMERATION_BUFFER;
+
+#define LSAP_DB_OBJECT_TYPE_COUNT 0x00000005L
+
+//
+// LSA Database Object Type-specific attribute names and values. If
+// supplied on a call to LsapDbCreateObject, they will be stored with
+// the object.
+//
+
+typedef struct _LSAP_DB_ATTRIBUTE {
+
+ PUNICODE_STRING AttributeName;
+ PVOID AttributeValue;
+ ULONG AttributeValueLength;
+ BOOLEAN MemoryAllocated;
+
+} LSAP_DB_ATTRIBUTE, *PLSAP_DB_ATTRIBUTE;
+
+//
+// LSA Database Object General Information.
+//
+
+typedef struct _LSAP_DB_OBJECT_INFORMATION {
+
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+ LSAP_DB_OBJECT_TYPE_ID ContainerTypeId;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PLSAP_DB_ATTRIBUTE TypeSpecificAttributes;
+ PSID Sid;
+
+} LSAP_DB_OBJECT_INFORMATION, *PLSAP_DB_OBJECT_INFORMATION;
+
+//
+// Default System Access assigned to Account objects
+//
+
+#define LSAP_DB_ACCOUNT_DEFAULT_SYS_ACCESS ((ULONG) 0L);
+
+//
+// LSA Database Account Object Information
+//
+
+typedef struct _LSAP_DB_ACCOUNT_INFORMATION {
+
+ QUOTA_LIMITS QuotaLimits;
+ PRIVILEGE_SET Privileges;
+
+} LSAP_DB_ACCOUNT_INFORMATION, *PLSAP_DB_ACCOUNT_INFORMATION;
+
+//
+// LSA Database Change Account Privilege Mode
+//
+
+typedef enum _LSAP_DB_CHANGE_PRIVILEGE_MODE {
+ AddPrivileges = 1,
+ RemovePrivileges
+
+} LSAP_DB_CHANGE_PRIVILEGE_MODE;
+
+//
+// Self-Relative Unicode String Structure.
+//
+
+typedef struct _LSAP_DB_MULTI_UNICODE_STRING {
+
+ ULONG Entries;
+ UNICODE_STRING UnicodeStrings[1];
+
+} LSAP_DB_MULTI_UNICODE_STRING, *PLSAP_DB_MULTI_UNICODE_STRING;
+
+//
+// LSA Database Object SubKey names in Unicode Form
+//
+
+typedef enum _LSAP_DB_NAMES {
+
+ SecDesc = 0,
+ Privilgs,
+ Sid,
+ Name,
+ AdminMod,
+ OperMode,
+ QuotaLim,
+ DefQuota,
+ QuAbsMin,
+ QuAbsMax,
+ AdtLog,
+ AdtEvent,
+ PrDomain,
+ EnPasswd,
+ Policy,
+ Accounts,
+ Domains,
+ Secrets,
+ CurrVal,
+ OldVal,
+ CupdTime,
+ OupdTime,
+ WkstaMgr,
+ PolAdtLg,
+ PolAdtEv,
+ PolAcDmN,
+ PolAcDmS,
+ PolPrDmN,
+ PolPrDmS,
+ PolPdAcN,
+ PolSrvRo,
+ PolRepSc,
+ PolRepAc,
+ PolRevision,
+ PolDefQu,
+ PolMod,
+ PolPromot,
+ PolAdtFL,
+ PolState,
+ PolNxPxF,
+ ActSysAc,
+ TrDmName,
+ TrDmSid,
+ TrDmAcN,
+ TrDmCtN,
+ TrDmPxOf,
+ TrDmCtEn,
+ AuditLog,
+ AuditLogMaxSize,
+ AuditRecordRetentionPeriod,
+ DummyLastName
+
+} LSAP_DB_NAMES;
+
+typedef struct _LSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO {
+
+ ULONG SystemAccess;
+ QUOTA_LIMITS QuotaLimits;
+ PPRIVILEGE_SET PrivilegeSet;
+
+} LSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO, *PLSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO;
+
+UNICODE_STRING LsapDbNames[DummyLastName];
+UNICODE_STRING LsapDbObjectTypeNames[DummyLastObject];
+
+
+//
+// Installed, absolute minimum and absolute maximum Quota Limits.
+//
+
+QUOTA_LIMITS LsapDbInstalledQuotaLimits;
+QUOTA_LIMITS LsapDbAbsMinQuotaLimits;
+QUOTA_LIMITS LsapDbAbsMaxQuotaLimits;
+
+//
+// LSA Database Exported Function Prototypes
+//
+// NOTE: These are callable only from the LSA
+//
+
+BOOLEAN
+LsapDbIsServerInitialized(
+ );
+
+NTSTATUS
+LsapDbOpenPolicy(
+ IN PLSAPR_SERVER_NAME SystemName OPTIONAL,
+ IN OPTIONAL PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE PolicyHandle,
+ IN BOOLEAN TrustedClient
+ );
+
+NTSTATUS
+LsapDbOpenTrustedDomain(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE TrustedDomainHandle,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbOpenObject(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG Options,
+ OUT PLSAPR_HANDLE LsaHandle
+ );
+
+NTSTATUS
+LsapDbCreateObject(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG CreateDisposition,
+ IN ULONG Options,
+ IN OPTIONAL PLSAP_DB_ATTRIBUTE TypeSpecificAttributes,
+ IN ULONG TypeSpecificAttributeCount,
+ OUT PLSAPR_HANDLE LsaHandle
+ );
+
+NTSTATUS
+LsapDbCloseObject(
+ IN PLSAPR_HANDLE ObjectHandle,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbDeleteObject(
+ IN LSAPR_HANDLE ObjectHandle
+ );
+
+NTSTATUS
+LsapDbReferenceObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbDereferenceObject(
+ IN OUT PLSAPR_HANDLE ObjectHandle,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN ULONG Options,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
+ IN NTSTATUS PreliminaryStatus
+ );
+
+NTSTATUS
+LsapDbReadAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PUNICODE_STRING AttributeNameU,
+ IN OPTIONAL PVOID AttributeValue,
+ IN OUT PULONG AttributeValueLength
+ );
+
+NTSTATUS
+LsapDbWriteAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PUNICODE_STRING AttributeNameU,
+ IN PVOID AttributeValue,
+ IN ULONG AttributeValueLength
+ );
+
+NTSTATUS
+LsapDbWriteAttributesObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PLSAP_DB_ATTRIBUTE Attributes,
+ IN ULONG AttributeCount
+ );
+
+NTSTATUS
+LsapDbReadAttributesObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN OUT PLSAP_DB_ATTRIBUTE Attributes,
+ IN ULONG AttributeCount
+ );
+
+NTSTATUS
+LsapDbDeleteAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PUNICODE_STRING AttributeNameU
+ );
+
+NTSTATUS
+LsapDbReferencesObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ OUT PULONG ReferenceCount
+ );
+
+NTSTATUS
+LsapDbQueryInformationAccounts(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PULONG PrivilegeCount,
+ OUT PLUID_AND_ATTRIBUTES *Privileges,
+ OUT PQUOTA_LIMITS QuotaLimits,
+ OUT PULONG SystemAccess
+ );
+
+NTSTATUS
+LsapDbEnableNonTrustedAccess(
+ );
+
+NTSTATUS
+LsapDbDisableNonTrustedAccess(
+ );
+
+NTSTATUS
+LsapDbOpenTransaction(
+ );
+
+NTSTATUS
+LsapDbApplyTransaction(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG Options,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType
+ );
+
+NTSTATUS
+LsapDbAbortTransaction(
+ );
+
+BOOLEAN
+LsapDbOpenedTransaction(
+ );
+
+NTSTATUS
+LsapDbSidToLogicalNameObject(
+ IN PSID Sid,
+ OUT PUNICODE_STRING LogicalNameU
+ );
+
+NTSTATUS
+LsapDbMakeTemporaryObject(
+ IN LSAPR_HANDLE ObjectHandle
+ );
+
+NTSTATUS
+LsapDbChangePrivilegesAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN LSAP_DB_CHANGE_PRIVILEGE_MODE ChangeMode,
+ IN BOOLEAN AllPrivileges,
+ IN OPTIONAL PPRIVILEGE_SET Privileges
+ );
+
+
+NTSTATUS
+LsapDbEnumerateSids(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsapDbFindNextSid(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ OUT PLSAPR_SID *NextSid
+ );
+
+NTSTATUS
+LsapDbEnumeratePrivileges(
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsapDbEnumerateNames(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAP_DB_NAME_ENUMERATION_BUFFER DbEnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsapDbFindNextName(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ OUT PLSAPR_UNICODE_STRING Name
+ );
+
+NTSTATUS
+LsapDbAcquireLock(
+ );
+
+VOID
+LsapDbReleaseLock(
+ );
+
+BOOLEAN LsapDbIsLocked();
+
+NTSTATUS
+LsapDbSetStates(
+ IN ULONG DesiredStates
+ );
+
+NTSTATUS
+LsapDbResetStates(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG Options,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
+ IN NTSTATUS PreliminaryStatus
+ );
+
+NTSTATUS
+LsapDbInitializeServer(
+ IN ULONG Pass
+ );
+
+NTSTATUS
+LsapDbInstallRegistry(
+ );
+
+//
+// These routines may someday migrate to Rtl runtime library. Their
+// names have Lsap Prefixes only temporarily, so that they can be located
+// easily.
+//
+
+// Options for LsapRtlAddPrivileges
+
+#define RTL_COMBINE_PRIVILEGE_ATTRIBUTES ((ULONG) 0x00000001L)
+#define RTL_SUPERSEDE_PRIVILEGE_ATTRIBUTES ((ULONG) 0x00000002L)
+
+NTSTATUS
+LsapRtlAddPrivileges(
+ IN PPRIVILEGE_SET ExistingPrivileges,
+ IN PPRIVILEGE_SET PrivilegesToAdd,
+ IN OPTIONAL PPRIVILEGE_SET UpdatedPrivileges,
+ IN PULONG UpdatedPrivilegesSize,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapRtlRemovePrivileges(
+ IN PPRIVILEGE_SET ExistingPrivileges,
+ IN PPRIVILEGE_SET PrivilegesToRemove,
+ IN OPTIONAL PPRIVILEGE_SET UpdatedPrivileges,
+ IN PULONG UpdatedPrivilegesSize
+ );
+
+PLUID_AND_ATTRIBUTES
+LsapRtlGetPrivilege(
+ IN PLUID_AND_ATTRIBUTES Privilege,
+ IN PPRIVILEGE_SET Privileges
+ );
+
+NTSTATUS
+LsapRtlCopyUnicodeString(
+ IN PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ );
+
+BOOLEAN
+LsapRtlPrefixSid(
+ IN PSID PrefixSid,
+ IN PSID Sid
+ );
+
+ULONG
+LsapDbGetSizeTextSid(
+ IN PSID Sid
+ );
+
+NTSTATUS
+LsapDbSidToTextSid(
+ IN PSID Sid,
+ OUT PSZ TextSid
+ );
+
+NTSTATUS
+LsapDbSidToUnicodeSid(
+ IN PSID Sid,
+ OUT PUNICODE_STRING SidU,
+ IN BOOLEAN AllocateDestinationString
+ );
+
+
+NTSTATUS
+LsapDbInitializeWellKnownValues();
+
+NTSTATUS
+LsapDbVerifyInformationObject(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation
+ );
+
+/*++
+
+BOOLEAN
+LsapDbIsValidTypeObject(
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
+ )
+
+Routine Description:
+
+ This macro function determines if a given Object Type Id is valid.
+
+Arguments:
+
+ ObjectTypeId - Object Type Id.
+
+Return Values:
+
+ BOOLEAN - TRUE if object type id is valid, else FALSE.
+
+--*/
+
+#define LsapDbIsValidTypeObject(ObjectTypeId) \
+ (((ObjectTypeId) > NullObject) && \
+ ((ObjectTypeId) < DummyLastObject))
+
+
+NTSTATUS
+LsapDbGetRequiredAccessQueryPolicy(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ OUT PACCESS_MASK RequiredAccess
+ );
+
+
+NTSTATUS
+LsapDbVerifyInfoQueryPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ OUT PACCESS_MASK RequiredAccess
+ );
+
+NTSTATUS
+LsapDbVerifyInfoSetPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_POLICY_INFORMATION PolicyInformation,
+ OUT PACCESS_MASK RequiredAccess
+ );
+
+BOOLEAN
+LsapDbValidInfoPolicy(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OPTIONAL PLSAPR_POLICY_INFORMATION PolicyInformation
+ );
+
+NTSTATUS
+LsapDbVerifyInfoQueryTrustedDomain(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN BOOLEAN Trusted,
+ OUT PACCESS_MASK RequiredAccess
+ );
+
+NTSTATUS
+LsapDbVerifyInfoSetTrustedDomain(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation,
+ IN BOOLEAN Trusted,
+ OUT PACCESS_MASK RequiredAccess
+ );
+
+BOOLEAN
+LsapDbValidInfoTrustedDomain(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN OPTIONAL PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
+ );
+
+NTSTATUS
+LsapDbMakeUnicodeAttribute(
+ IN OPTIONAL PUNICODE_STRING UnicodeValue,
+ IN PUNICODE_STRING AttributeName,
+ OUT PLSAP_DB_ATTRIBUTE Attribute
+ );
+
+NTSTATUS
+LsapDbMakeMultiUnicodeAttribute(
+ OUT PLSAP_DB_ATTRIBUTE Attribute,
+ IN PUNICODE_STRING AttributeName,
+ IN PUNICODE_STRING UnicodeNames,
+ IN ULONG Entries
+ );
+
+NTSTATUS
+LsapDbCopyUnicodeAttribute(
+ OUT PUNICODE_STRING OutputString,
+ IN PLSAP_DB_ATTRIBUTE Attribute,
+ IN BOOLEAN SelfRelative
+ );
+
+NTSTATUS
+LsapDbCopyMultiUnicodeAttribute(
+ IN PLSAP_DB_ATTRIBUTE Attribute,
+ OUT PULONG Entries,
+ OUT PUNICODE_STRING *OutputString
+ );
+
+NTSTATUS
+LsapDbMakeSidAttribute(
+ IN PSID Sid,
+ IN PUNICODE_STRING AttributeName,
+ OUT PLSAP_DB_ATTRIBUTE Attribute
+ );
+
+NTSTATUS
+LsapDbReadAttribute(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN OUT PLSAP_DB_ATTRIBUTE Attribute
+ );
+
+NTSTATUS
+LsapDbFreeAttributes(
+ IN ULONG Count,
+ IN PLSAP_DB_ATTRIBUTE Attributes
+ );
+
+/*++
+
+VOID
+LsapDbInitializeAttribute(
+ IN PLSAP_DB_ATTRIBUTE AttributeP,
+ IN PUNICODE_STRING AttributeNameP,
+ IN OPTIONAL PVOID AttributeValueP,
+ IN ULONG AttributeValueLengthP,
+ IN BOOLEAN MemoryAllocatedP
+ )
+
+Routine Description:
+
+ This macro function initialize an Lsa Database Object Attribute
+ structure. No validation is done.
+
+Arguments:
+
+ AttributeP - Pointer to Lsa Database Attribute structure to be
+ initialized.
+
+ AttributeNameP - Pointer to Unicode String containing the attribute's
+ name.
+
+ AttributeValueP - Pointer to the attribute's value. NULL may be
+ specified.
+
+ AttributeValueLengthP - Length of the attribute's value in bytes.
+
+ MemoryAllocatedP - TRUE if memory is allocated by MIDL_user_allocate
+ within the LSA Server code (not by RPC server stubs), else FALSE.
+
+Return Values:
+
+ None.
+
+--*/
+
+#define LsapDbInitializeAttribute( \
+ AttributeP, \
+ AttributeNameP, \
+ AttributeValueP, \
+ AttributeValueLengthP, \
+ MemoryAllocatedP \
+ ) \
+ \
+{ \
+ (AttributeP)->AttributeName = AttributeNameP; \
+ (AttributeP)->AttributeValue = AttributeValueP; \
+ (AttributeP)->AttributeValueLength = (ULONG) (AttributeValueLengthP); \
+ (AttributeP)->MemoryAllocated = MemoryAllocatedP; \
+}
+
+
+NTSTATUS
+LsapDbGetPrivilegesAndQuotas(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PULONG PrivilegeCount,
+ OUT PLUID_AND_ATTRIBUTES *Privileges,
+ OUT PQUOTA_LIMITS QuotaLimits
+ );
+
+
+NTSTATUS
+LsapDbNotifyRoleChangePolicy(
+ IN POLICY_LSA_SERVER_ROLE NewRole
+ );
+
+VOID
+LsapDbEnableReplicatorNotification();
+
+VOID
+LsapDbDisableReplicatorNotification();
+
+NTSTATUS
+LsapDbVerifyHandle(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG Options,
+ IN LSAP_DB_OBJECT_TYPE_ID ExpectedObjectTypeId
+ );
+
+NTSTATUS
+LsapDbQueryAllInformationAccounts(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PLSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO AccountInfo
+ );
+
+#endif // _LSA_DB_
diff --git a/private/lsa/server/dbaccnt.c b/private/lsa/server/dbaccnt.c
new file mode 100644
index 000000000..28fcac651
--- /dev/null
+++ b/private/lsa/server/dbaccnt.c
@@ -0,0 +1,4017 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbaccnt.c
+
+Abstract:
+
+ LSA - Database - Account Object Private API Workers
+
+ NOTE: This module should remain as portable code that is independent
+ of the implementation of the LSA Database. As such, it is
+ permitted to use only the exported LSA Database interfaces
+ contained in db.h and NOT the private implementation
+ dependent functions in dbp.h.
+
+Author:
+
+ Scott Birrell (ScottBi) April 29, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Private Datatypes //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// The following structures define output for the LsarQueryInformationAccount()
+// API. Curerently, this API is internal. If made external, these structures
+// should be moved to lsarpc.idl and annotated with MIDL qualifiers [..].
+//
+
+//
+// This data type defines the following information classes that may be
+// queried or set.
+//
+
+typedef enum _ACCOUNT_INFORMATION_CLASS {
+
+ AccountSystemAccessInformation = 1,
+ AccountPrivilegeInformation,
+ AccountQuotaInformation
+
+} ACCOUNT_INFORMATION_CLASS, *PACCOUNT_INFORMATION_CLASS;
+
+typedef PRIVILEGE_SET LSAPR_ACCOUNT_PRIVILEGE_INFO;
+typedef QUOTA_LIMITS LSAPR_ACCOUNT_QUOTA_INFO;
+typedef ULONG LSAPR_ACCOUNT_SYSTEM_ACCESS_INFO;
+
+typedef union _LSAPR_ACCOUNT_INFO {
+
+ LSAPR_ACCOUNT_PRIVILEGE_INFO AccountPrivilegeInfo;
+ LSAPR_ACCOUNT_QUOTA_INFO AccountQuotaInfo;
+ LSAPR_ACCOUNT_SYSTEM_ACCESS_INFO AccountSystemAccessInfo;
+
+} LSAPR_ACCOUNT_INFO, *PLSAPR_ACCOUNT_INFO;
+
+#define LsapDbFirstAccount() \
+ ((PLSAP_DB_ACCOUNT) LsapDbAccountList.Links.Flink)
+
+#define LsapDbNextAccount( Account ) \
+ ((PLSAP_DB_ACCOUNT) Account->Links.Flink)
+
+
+#define LSAP_DB_BUILD_ACCOUNT_LIST_LENGTH ((ULONG) 0x00001000L)
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Private Function Prototypes //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarQueryInformationAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN ACCOUNT_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_ACCOUNT_INFO *AccountInformation
+ );
+
+NTSTATUS
+LsapDbQueryInformationAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN ACCOUNT_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_ACCOUNT_INFO *AccountInformation
+ );
+
+NTSTATUS
+LsapDbQueryAllInformationAccounts(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PLSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO AccountInfo
+ );
+
+NTSTATUS
+LsapDbSlowQueryAllInformationAccounts(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PLSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO AccountInfo
+ );
+
+NTSTATUS
+LsapDbSlowQueryInformationAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN ACCOUNT_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_ACCOUNT_INFO *AccountInformation
+ );
+
+NTSTATUS
+LsapDbSlowQueryPrivilegesAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PLSAPR_PRIVILEGE_SET *Privileges
+ );
+
+NTSTATUS
+LsapDbSlowQueryQuotasAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PQUOTA_LIMITS QuotaLimits
+ );
+
+NTSTATUS
+LsapDbSlowQuerySystemAccessAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PULONG SystemAccess
+ );
+
+NTSTATUS
+LsapDbLookupAccount(
+ IN PSID AccountSid,
+ OUT PLSAP_DB_ACCOUNT *Account
+ );
+
+NTSTATUS
+LsapDbUpdateSystemAccessAccount(
+ IN PLSAPR_SID AccountSid,
+ IN PULONG SystemAccess
+ );
+
+NTSTATUS
+LsapDbUpdatePrivilegesAccount(
+ IN PLSAPR_SID AccountSid,
+ IN OPTIONAL PPRIVILEGE_SET Privileges
+ );
+
+NTSTATUS
+LsapDbUpdateQuotasAccount(
+ IN PLSAPR_SID AccountSid,
+ IN PQUOTA_LIMITS QuotaLimits
+ );
+
+NTSTATUS
+LsapDbCreateAccountList(
+ OUT PLSAP_DB_ACCOUNT_LIST AccountList
+ );
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Private Global Data //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+LSAP_DB_ACCOUNT_LIST LsapDbAccountList;
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Code //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarCreateAccount(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID AccountSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE AccountHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaCreateAccount API.
+
+ The LsaCreateAccount API creates a new Account Object. The account will
+ be opened with the specified accesses granted. The caller must
+ have POLICY_CREATE_ACCOUNT access to the Policy Object.
+
+ Note that no verification is done to ensure the SID actually represents
+ a valid user, group or alias in any trusted domain.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ AccountSid - Points to the SID of the account.
+
+ DesiredAccess - Specifies the accesses to be granted to the newly
+ created and opened account at this time.
+
+ AccountHandle - Receives a handle referencing the newly created
+ account. This handle is used on subsequent accesses to the
+ account object.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_EXISTS - An account object having the given Sid
+ already exists and has been opened because LSA_OBJECT_OPEN_IF
+ disposition has been specified. This is a warning only.
+
+ STATUS_OBJECT_NAME_COLLISION - An account object having the given Sid
+ already exists but has not been opened because LSA_OBJECT_CREATE
+ disposition has been specified. This is an error.
+
+ STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_ACCOUNT];
+ PLSAP_DB_ATTRIBUTE NextAttribute;
+ UNICODE_STRING LogicalNameU;
+ ULONG AttributeCount;
+ BOOLEAN ContainerReferenced = FALSE;
+
+ LogicalNameU.Length = 0;
+
+ //
+ // Set up the object's attributes specific to the Account object type.
+ // These are the Account Type and the Sid.
+ //
+
+ AttributeCount = 0;
+ NextAttribute = Attributes;
+
+ //
+ // Validate the Account Sid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!RtlValidSid( (PSID) AccountSid )) {
+
+ goto CreateAccountError;
+ }
+
+ Status = LsapDbMakeSidAttribute(
+ AccountSid,
+ &LsapDbNames[Sid],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateAccountError;
+ }
+
+ AttributeCount++;
+ NextAttribute++;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the PolicyHandle
+ // is valid and has the necessary access granted. Reference the Policy
+ // Object handle (as container object).
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_CREATE_ACCOUNT,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateAccountError;
+ }
+
+ ContainerReferenced = TRUE;
+
+ //
+ // Construct the Logical Name (Internal LSA Database Name) of the
+ // account object. The Logical Name is constructed from the account
+ // Sid by extracting the Relative Id (lowest subauthority) and converting
+ // it to an 8-digit numeric Unicode String in which leading zeros are
+ // added if needed.
+ //
+
+ Status = LsapDbSidToLogicalNameObject(AccountSid, &LogicalNameU);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateAccountError;
+ }
+
+ //
+ // Fill in the ObjectInformation structure. Initialize the
+ // embedded Object Attributes with the PolicyHandle as the
+ // Root Directory (Container Object) handle and the Logical Name
+ // of the account. Store the types of the object and its container.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ &LogicalNameU,
+ OBJ_CASE_INSENSITIVE,
+ PolicyHandle,
+ NULL
+ );
+
+ ObjectInformation.ObjectTypeId = AccountObject;
+ ObjectInformation.ContainerTypeId = PolicyObject;
+ ObjectInformation.Sid = AccountSid;
+
+ //
+ // Create the Account Object. We fail if the object already exists.
+ // Note that the object create routine performs a Database transaction.
+ // If caching is supported, the object will also be added to the cache.
+ //
+
+ Status = LsapDbCreateObject(
+ &ObjectInformation,
+ DesiredAccess,
+ LSAP_DB_OBJECT_CREATE,
+ 0,
+ Attributes,
+ AttributeCount,
+ AccountHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateAccountError;
+ }
+
+CreateAccountFinish:
+
+ //
+ // If necessary, release the LSA Database lock.
+ //
+
+ if (ContainerReferenced) {
+
+ LsapDbReleaseLock();
+ }
+
+ //
+ // If necessary, free the Unicode String buffer allocated for the Logical Name
+ //
+
+ if (LogicalNameU.Length > 0) {
+
+ RtlFreeUnicodeString(&LogicalNameU);
+ }
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (*AccountHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+
+ return( Status );
+
+CreateAccountError:
+
+ //
+ // If necessary, dereference the Container Object, release the LSA
+ // Database Lock and return.
+ //
+
+ if (ContainerReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ ContainerReferenced = FALSE;
+ }
+
+ goto CreateAccountFinish;
+}
+
+
+NTSTATUS
+LsarOpenAccount(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID AccountSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE AccountHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the LsaOpenAccount
+ API.
+
+ The LsaOpenAccount API opens an account object in the Lsa Database of the
+ target system. An account must be opened before any operation can be
+ performed, including deletion of the account. A handle to the account
+ object is returned for use on subsequent API calls that access the
+ account. Before calling this API, the caller must have connected to
+ the target system's LSA and opened the LsaDatabase object by means
+ of a preceding call to LsaOpenPolicy.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ AccountSid - Pointer to the account's Sid.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the Account object. These access types
+ are reconciled with the Discretionary Access Control List of the
+ object to determine whether the accesses will be granted or denied.
+
+ AccountHandle - Pointer to location in which a handle to the opened
+ account object will be returned if the call succeeds.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - There is no account object in the
+ target system's LSA Database having the specified AccountSid.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ UNICODE_STRING LogicalNameU;
+ BOOLEAN ContainerReferenced = FALSE;
+ BOOLEAN AcquiredLock = FALSE;
+
+ //
+ // Validate the Account Sid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!RtlValidSid( AccountSid )) {
+
+ goto OpenAccountError;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle
+ // (container object handle) is valid, and is of the expected type.
+ // Reference the container object handle. This reference remains in
+ // effect until the child object handle is closed.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenAccountError;
+ }
+
+ AcquiredLock = TRUE;
+ ContainerReferenced =TRUE;
+
+ //
+ // Setup Object Information prior to calling the Object
+ // Open routine. The Object Type, Container Object Type and
+ // Logical Name (derived from the Sid) need to be filled in.
+ //
+
+ ObjectInformation.ObjectTypeId = AccountObject;
+ ObjectInformation.ContainerTypeId = PolicyObject;
+ ObjectInformation.Sid = AccountSid;
+
+ //
+ // Construct the Logical Name (Internal LSA Database Name) of the
+ // account object. The Logical Name is constructed from the account
+ // Sid by extracting the Relative Id (lowest subauthority) and converting
+ // it to an 8-digit numeric Unicode String in which leading zeros are
+ // added if needed.
+ //
+
+ Status = LsapDbSidToLogicalNameObject(AccountSid,&LogicalNameU);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenAccountError;
+ }
+ //
+ // Initialize the Object Attributes. The Container Object Handle and
+ // Logical Name (Internal Name) of the object must be set up.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ &LogicalNameU,
+ 0,
+ PolicyHandle,
+ NULL
+ );
+
+ //
+ // Open the specific account object. Note that the account object
+ // handle returned is an RPC Context Handle.
+ //
+
+ Status = LsapDbOpenObject(
+ &ObjectInformation,
+ DesiredAccess,
+ 0,
+ AccountHandle
+ );
+
+ RtlFreeUnicodeString( &LogicalNameU );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenAccountError;
+ }
+
+OpenAccountFinish:
+
+ //
+ // If necessary, release the LSA Database lock.
+ //
+
+ if (AcquiredLock) {
+
+ LsapDbReleaseLock();
+ AcquiredLock = FALSE;
+ }
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (*AccountHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+
+ return( Status );
+
+OpenAccountError:
+
+ //
+ // If necessary, dereference the Container Object handle. Note that
+ // this is only done in the error case. In the non-error case, the
+ // Container handle stays referenced until the Account object is
+ // closed.
+ //
+
+ if (ContainerReferenced) {
+
+ *AccountHandle = NULL;
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ 0,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ goto OpenAccountFinish;
+
+}
+
+
+NTSTATUS
+LsarEnumerateAccounts(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaEnumerateAccounts API.
+
+ The LsaEnumerateAccounts API returns information about the accounts
+ in the target system's Lsa Database. This call requires
+ LSA_ENUMERATE_ACCOUNTS access to the Policy object. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information. To
+ support this feature, the caller is provided with a handle that can be
+ used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0. On each
+ subsequent call, the value returned by the preceding call should be passed
+ in unchanged. The enumeration is complete when the warning
+ STATUS_NO_MORE_ENTRIES is returned.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationBuffer - Pointer to an enumeration structure that will receive
+ a count of the accounts enumerated on this call and a pointer to
+ an array of entries containing information for each enumerated
+ account.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if no objects are enumerated because the
+ EnumerationContext value passed in is too high.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer;
+ ULONG MaxLength;
+
+
+ //
+ // If no Enumeration Structure or index is provided, return an error.
+ //
+
+ if ( !ARGUMENT_PRESENT(EnumerationBuffer) ||
+ !ARGUMENT_PRESENT(EnumerationBuffer) ) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Initialize the internal Lsa Database Enumeration Buffer, and
+ // the provided Enumeration Buffer to NULL.
+ //
+
+ DbEnumerationBuffer.EntriesRead = 0;
+ DbEnumerationBuffer.Sids = NULL;
+ EnumerationBuffer->EntriesRead = 0;
+ EnumerationBuffer->Information = NULL;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Limit the enumeration length except for trusted callers
+ //
+
+ if ( !((LSAP_DB_HANDLE)PolicyHandle)->Trusted &&
+ (PreferedMaximumLength > LSA_MAXIMUM_ENUMERATION_LENGTH)
+ ) {
+ MaxLength = LSA_MAXIMUM_ENUMERATION_LENGTH;
+ } else {
+ MaxLength = PreferedMaximumLength;
+ }
+
+ //
+ // Call general Sid enumeration routine.
+ //
+
+ Status = LsapDbEnumerateSids(
+ PolicyHandle,
+ AccountObject,
+ EnumerationContext,
+ &DbEnumerationBuffer,
+ MaxLength
+ );
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ }
+
+ //
+ // Copy the enumerated information to the output. We can use the
+ // information actually returned by LsapDbEnumerateSids because it
+ // happens to be in exactly the correct form.
+ //
+
+ EnumerationBuffer->EntriesRead = DbEnumerationBuffer.EntriesRead;
+ EnumerationBuffer->Information =
+ (PLSAPR_ACCOUNT_INFORMATION) DbEnumerationBuffer.Sids;
+
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsarEnumeratePrivilegesAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PLSAPR_PRIVILEGE_SET *Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaEnumeratePrivilegesOfAccount API.
+
+ The LsaEnumeratePrivilegesOfAccount API obtains information which
+ describes the privileges assigned to an account. This call requires
+ ACCOUNT_VIEW access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose privilege
+ information is to be obtained. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ Privileges - Receives a pointer to a buffer containing the Privilege
+ Set. The Privilege Set is an array of structures, one for each
+ privilege. Each structure contains the LUID of the privilege and
+ a mask of the privilege's attributes.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ return(LsarQueryInformationAccount(
+ AccountHandle,
+ AccountPrivilegeInformation,
+ (PLSAPR_ACCOUNT_INFO *) Privileges
+ ));
+}
+
+
+NTSTATUS
+LsarAddPrivilegesToAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN PLSAPR_PRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaAddPrivilegesToAccount API.
+
+ The LsaAddPrivilegesToAccount API adds privileges and their attributes
+ to an account object. If any provided privilege is already assigned
+ to the account object, the attributes of that privilege are replaced
+ by the newly rpovided values. This API call requires
+ LSA_ACCOUNT_ADJUST_PRIVILEGES access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object to which
+ privileges are to be added. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ Privileges - Points to a set of privileges (and their attributes) to
+ be assigned to the account.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+
+--*/
+
+{
+ return(LsapDbChangePrivilegesAccount(
+ AccountHandle,
+ AddPrivileges,
+ FALSE,
+ (PPRIVILEGE_SET) Privileges
+ ));
+}
+
+
+NTSTATUS
+LsarRemovePrivilegesFromAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN BOOLEAN AllPrivileges,
+ IN OPTIONAL PLSAPR_PRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the RPC server worker routine for the
+ LsaRemovePrivilegesFromAccount API.
+
+ The LsaRemovePrivilegesFromAccount API removes privileges from an
+ account object. This API call requires LSA_ACCOUNT_ADJUST_PRIVILEGES
+ access to the account object. Note that if all privileges are removed
+ from the account object, the account object remains in existence until
+ deleted explicitly via a call to the LsaDeleteAccount API.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object to which
+ privileges are to be removed. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ AllPrivileges - If TRUE, then all privileges are to be removed from
+ the account. In this case, the Privileges parameter must be
+ specified as NULL. If FALSE, the Privileges parameter specifies
+ the privileges to be removed, and must be non NULL.
+
+ Privileges - Optionally points to a set of privileges (and their
+ attributes) to be removed from the account object. The attributes
+ fields of this structure are ignored. This parameter must
+ be specified as non-NULL if and only if AllPrivileges is set to
+ FALSE.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+
+ STATUS_INVALID_PARAMETER - The optional Privileges paraemter was
+ specified as NULL and AllPrivileges was set to FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Verify that a meaningful combination of AllPrivileges and Privileges
+ // has been specified.
+ //
+
+ if (AllPrivileges) {
+
+ if (Privileges != NULL) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ } else {
+
+ if (Privileges == NULL) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Remove the privileges requested.
+ //
+
+ Status = LsapDbChangePrivilegesAccount(
+ AccountHandle,
+ RemovePrivileges,
+ AllPrivileges,
+ (PPRIVILEGE_SET) Privileges
+ );
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+LsapDbChangePrivilegesAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN LSAP_DB_CHANGE_PRIVILEGE_MODE ChangeMode,
+ IN BOOLEAN AllPrivileges,
+ IN OPTIONAL PPRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This function changes the privileges assigned to an account. It is
+ called only by LsarAddPrivilegesToAccount and LsarRemovePrivilegesFrom-
+ Account.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ AccountHandle - Handle to open Account object obtained from LsaOpenAccount.
+
+ ChangeMode - Specifies the change mode
+
+ AddPrivileges - Add the privileges
+ RemovePrivileges - Delete the privileges
+
+ AllPrivileges - If removing privileges from an account and this boolean
+ is set to TRUE, all privileges are to be removed. In this case,
+ the Privileges parameter must be set to NULL. In all other cases,
+ AllPrivileges must be set to FALSE and Privileges must be non-NULL.
+
+ Privileges - Specifies set of privileges to be changed. This parameter
+ must be set to NULL if and only if removing all privileges.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ ULONG ExistingPrivilegesSize;
+ ULONG UpdatedPrivilegesSize;
+ LSAPR_HANDLE SubKeyHandle = NULL;
+ PPRIVILEGE_SET ExistingPrivileges = NULL;
+ PPRIVILEGE_SET UpdatedPrivileges = NULL;
+ BOOLEAN TransactionAbort = FALSE;
+ ULONG AuditEventId;
+ PLUID_AND_ATTRIBUTES Luids = NULL;
+ BOOLEAN ObjectReferenced = FALSE;
+ PLSAPR_SID AccountSid = NULL;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the Account Object handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle and open a database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ AccountHandle,
+ ACCOUNT_ADJUST_PRIVILEGES,
+ AccountObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ChangePrivilegesError;
+ }
+
+ ObjectReferenced = TRUE;
+ UpdatedPrivilegesSize = 0L;
+
+ //
+ // Except in the case where AllPrivileges == TRUE on a remove call,
+ // we need to read the existing privilege set.
+ //
+
+ if (!(AllPrivileges && (ChangeMode == RemovePrivileges))) {
+
+ //
+ // Query size of buffer needed for the existing Privileges.
+ // Read the Account Object's Privileges data from the LSA Database
+ //
+
+ ExistingPrivilegesSize = 0;
+
+ Status = LsapDbReadAttributeObject(
+ AccountHandle,
+ &LsapDbNames[Privilgs],
+ NULL,
+ &ExistingPrivilegesSize
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // The only error permitted is STATUS_OBJECT_NAME_NOT_FOUND
+ // because the account object does not have any privileges
+ // assigned and has no Privilgs attribute.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto ChangePrivilegesError;
+ }
+
+ //
+ // Account has no existing privileges.
+ //
+
+ ExistingPrivileges = NULL;
+
+ } else {
+
+ //
+ // Account already has privileges. Allocate buffer for reading
+ // the existing privilege set and read them in.
+ //
+
+ ExistingPrivileges = LsapAllocateLsaHeap( ExistingPrivilegesSize );
+
+ if (ExistingPrivileges == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ChangePrivilegesError;
+ }
+
+ Status = LsapDbReadAttributeObject(
+ AccountHandle,
+ &LsapDbNames[Privilgs],
+ ExistingPrivileges,
+ &ExistingPrivilegesSize
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ChangePrivilegesError;
+ }
+ }
+
+ //
+ // Now query the size of buffer required for the updated privilege
+ // set
+ //
+
+ if (ChangeMode == AddPrivileges) {
+
+ Status = LsapRtlAddPrivileges(
+ ExistingPrivileges,
+ Privileges,
+ NULL,
+ &UpdatedPrivilegesSize,
+ RTL_SUPERSEDE_PRIVILEGE_ATTRIBUTES
+ );
+
+ } else {
+
+ Status = LsapRtlRemovePrivileges(
+ ExistingPrivileges,
+ Privileges,
+ NULL,
+ &UpdatedPrivilegesSize
+ );
+ }
+
+ //
+ // The warning STATUS_BUFFER_OVERFLOW is the only one expected.
+ // Note that STATUS_SUCCESS is possible where all privileges are
+ // removed.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_BUFFER_OVERFLOW) {
+
+ goto ChangePrivilegesError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Now allocate buffer for the updated privilege set (if non NULL).
+ // Note that we must check for the case where all privileges are being
+ // removed but AllPrivileges was FALSE.
+ //
+
+ if (UpdatedPrivilegesSize != 0) {
+
+ //
+ // Allocate memory for the updated value of the Privileges subkey
+ //
+
+ UpdatedPrivileges = MIDL_user_allocate( UpdatedPrivilegesSize );
+
+ if (UpdatedPrivileges == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ChangePrivilegesError;
+ }
+
+ //
+ // Now generate the updated privilege set.
+ //
+
+ if (ChangeMode == AddPrivileges) {
+
+ Status = LsapRtlAddPrivileges(
+ ExistingPrivileges,
+ Privileges,
+ UpdatedPrivileges,
+ &UpdatedPrivilegesSize,
+ RTL_SUPERSEDE_PRIVILEGE_ATTRIBUTES
+ );
+
+ } else {
+
+ Status = LsapRtlRemovePrivileges(
+ ExistingPrivileges,
+ Privileges,
+ UpdatedPrivileges,
+ &UpdatedPrivilegesSize
+ );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ChangePrivilegesError;
+ }
+ }
+ }
+
+ //
+ // If privileges remain, write the updated privilege set back to
+ // the LSA Database as the value of the Privilgs attribute of the
+ // account object. If no privileges remain, delete the Privilgs
+ // attribute.
+ //
+
+ if (UpdatedPrivilegesSize > 0) {
+
+ Status = LsapDbWriteAttributeObject(
+ AccountHandle,
+ &LsapDbNames[Privilgs],
+ UpdatedPrivileges,
+ UpdatedPrivilegesSize
+ );
+
+ } else {
+
+ Status = LsapDbDeleteAttributeObject(
+ AccountHandle,
+ &LsapDbNames[Privilgs]
+ );
+
+ //
+ // The only error permitted is STATUS_OBJECT_NAME_NOT_FOUND
+ // because the account object does not have any privileges
+ // assigned and so has no Privilgs attribute.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // If auditing of policy changes is enabled, generate an audit.
+ //
+
+ AccountSid = LsapDbSidFromHandle( AccountHandle );
+
+ if (LsapAdtAuditingPolicyChanges()) {
+
+ AuditEventId = ((ChangeMode == AddPrivileges) ?
+ SE_AUDITID_USER_RIGHT_ASSIGNED :
+ SE_AUDITID_USER_RIGHT_REMOVED);
+
+#if 0
+
+ //
+ // This code doesn't appear to do anything useful.
+ //
+
+ //
+ // Obtain list of the privileges being added or removed. If removing
+ // all privileges, pass a list of the existing privileges assigned
+ // to the account.
+ //
+
+ LuidCount = Privileges->PrivilegeCount;
+ Luids = Privileges->Privilege;
+
+ if (AllPrivileges) {
+
+ LuidCount = ExistingPrivileges->PrivilegeCount;
+ Luids = ExistingPrivileges->Privilege;
+ }
+
+#endif
+
+ //
+ // Audit the privilege set change. Ignore failures from Auditing.
+ //
+
+ IgnoreStatus = LsapAdtGenerateLsaAuditEvent(
+ AccountHandle,
+ SE_CATEGID_POLICY_CHANGE,
+ AuditEventId,
+ Privileges,
+ 1,
+ (PSID *) &AccountSid,
+ 0,
+ NULL,
+ NULL
+ );
+ }
+
+ //
+ // Update the Account Object Cache while holding the Lsa Database Lock.
+ // If the commit to backing storage below fails, caching will automatically
+ // be turned off.
+ //
+ // NOTE: A pointer to the UpdatedPrivileges buffer will be placed directly
+ // in the cached Account Object, so it should not be freed by this routine.
+ //
+
+ if (UpdatedPrivilegesSize > 0) {
+
+ IgnoreStatus = LsapDbUpdatePrivilegesAccount(
+ AccountSid,
+ UpdatedPrivileges
+ );
+
+ //
+ // If the update was unsuccessful, we must free the Updated Privileges
+ // buffer.
+ //
+
+ if (!NT_SUCCESS(IgnoreStatus)) {
+
+ MIDL_user_free( UpdatedPrivileges );
+ UpdatedPrivileges = NULL;
+ }
+
+ } else {
+
+ IgnoreStatus = LsapDbUpdatePrivilegesAccount(
+ AccountSid,
+ NULL
+ );
+ }
+
+ChangePrivilegesFinish:
+
+ //
+ // If necessary, free the ExistingPrivileges buffer.
+ //
+
+ if (ExistingPrivileges != NULL) {
+
+ LsapFreeLsaHeap(ExistingPrivileges);
+ ExistingPrivileges = NULL;
+ }
+
+ //
+ // If necessary, dereference the Account object, close the database
+ // transaction, release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ IgnoreStatus = LsapDbDereferenceObject(
+ &AccountHandle,
+ AccountObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbChange,
+ Status
+ );
+ }
+
+ return(Status);
+
+ChangePrivilegesError:
+
+ //
+ // If necessary, free the UpdatedPrivileges buffer.
+ //
+
+ if (UpdatedPrivileges != NULL) {
+
+ LsapFreeLsaHeap(UpdatedPrivileges);
+ UpdatedPrivileges = NULL;
+ }
+
+ goto ChangePrivilegesFinish;
+}
+
+
+NTSTATUS
+LsarGetQuotasForAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PQUOTA_LIMITS QuotaLimits
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsarGetQuotasForAccount API.
+
+ The LsaGetQuotasForAccount API obtains the quota limits for pageable and
+ non-pageable memory (in Kilobytes) and the maximum execution time (in
+ seconds) for any session logged on to the account specified by
+ AccountHandle. For each quota and explicit value is returned. This
+ call requires LSA_ACCOUNT_VIEW access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose quotas
+ are to be obtained. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ QuotaLimits - Pointer to structure in which the system resource
+ quota limits applicable to each session logged on to this account
+ will be returned. Note that all quotas, including those specified
+ as being the system default values, are returned as actual values.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAPR_ACCOUNT_INFO AccountInformation = NULL;
+
+ Status = LsarQueryInformationAccount(
+ AccountHandle,
+ AccountQuotaInformation,
+ &AccountInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GetQuotasError;
+ }
+
+ *QuotaLimits = *((PQUOTA_LIMITS) AccountInformation);
+
+GetQuotasFinish:
+
+ //
+ // If necessary, free the buffer in which the Account Information was
+ // returned.
+ //
+
+ if (AccountInformation != NULL) {
+
+ MIDL_user_free( AccountInformation );
+ AccountInformation = NULL;
+ }
+
+ return(Status);
+
+GetQuotasError:
+
+ RtlZeroMemory( QuotaLimits, sizeof(QUOTA_LIMITS) );
+ goto GetQuotasFinish;
+}
+
+
+NTSTATUS
+LsarSetQuotasForAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN PQUOTA_LIMITS QuotaLimits
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaSetQuotasForAccount API.
+
+ The LsaSetQuotasForAccount API sets the quota limits for pageable and
+ non-pageable memory (in Kilobytes) and the maximum execution time (in
+ seconds) for any session logged on to the account specified by
+ AccountHandle. For each quota an explicit value or the system default
+ may be specified. This call requires LSA_ACCOUNT_ADJUST_QUOTAS
+ access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose quotas
+ are to be set. This handle will have been returned from a prior
+ LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ QuotaLimits - Pointer to structure containing the system resource
+ quota limits applicable to each session logged on to this account.
+ A zero value specified in any field indicates that the current
+ System Default Quota Limit is to be applied.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ ULONG QuotaLimitsLength = sizeof (QUOTA_LIMITS);
+ QUOTA_LIMITS UpdatedQuotaLimits;
+ QUOTA_LIMITS DefaultQuotaLimits;
+ ULONG DefaultQuotaLimitsLength = sizeof (QUOTA_LIMITS);
+ BOOLEAN ObjectReferenced = FALSE;
+ PLSAPR_SID AccountSid = NULL;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the Account Object handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle and open a database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ AccountHandle,
+ ACCOUNT_ADJUST_QUOTAS,
+ AccountObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetQuotasError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Read the Default Quota Limits from the LSA Database Object
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbContainerFromHandle( AccountHandle ),
+ &LsapDbNames[DefQuota],
+ &DefaultQuotaLimits,
+ &DefaultQuotaLimitsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetQuotasError;
+ }
+
+
+ //
+ // Get the quota limits be to set. Where there are zero entries in the
+ // input, apply the defaults.
+ //
+
+ UpdatedQuotaLimits = *QuotaLimits;
+
+ if (QuotaLimits->PagedPoolLimit == 0) {
+
+ UpdatedQuotaLimits.PagedPoolLimit = DefaultQuotaLimits.PagedPoolLimit;
+ }
+
+ if (QuotaLimits->NonPagedPoolLimit == 0) {
+
+ UpdatedQuotaLimits.NonPagedPoolLimit =
+ DefaultQuotaLimits.NonPagedPoolLimit;
+ }
+
+ if (QuotaLimits->MinimumWorkingSetSize == 0) {
+
+ UpdatedQuotaLimits.MinimumWorkingSetSize =
+ DefaultQuotaLimits.MinimumWorkingSetSize;
+ }
+
+ if (QuotaLimits->MaximumWorkingSetSize == 0) {
+
+ UpdatedQuotaLimits.MaximumWorkingSetSize =
+ DefaultQuotaLimits.MaximumWorkingSetSize;
+ }
+
+ if (QuotaLimits->PagefileLimit == 0) {
+
+ UpdatedQuotaLimits.PagefileLimit = DefaultQuotaLimits.PagefileLimit;
+ }
+
+ //
+ // Write the updated information back
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ AccountHandle,
+ &LsapDbNames[QuotaLim],
+ &UpdatedQuotaLimits,
+ QuotaLimitsLength
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto SetQuotasError;
+ }
+
+ //
+ // Update the Account Object Cache while holding the Lsa Database Lock.
+ // If the commit to backing storage below fails, caching will automatically
+ // be turned off.
+ //
+
+ AccountSid = LsapDbSidFromHandle( AccountHandle );
+
+ IgnoreStatus = LsapDbUpdateQuotasAccount(
+ AccountSid,
+ &UpdatedQuotaLimits
+ );
+
+SetQuotasFinish:
+
+ //
+ // If necessary, dereference the Account object, close the database transaction,
+ // notify the LSA Database Replicator of the change, release the LSA
+ // Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &AccountHandle,
+ AccountObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbChange,
+ Status
+ );
+ }
+
+ return( Status );
+
+SetQuotasError:
+
+ goto SetQuotasFinish;
+}
+
+
+NTSTATUS
+LsarGetSystemAccessAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaGetSystemAccessAccount() service returns the System Access
+ account flags for an Account object.
+
+Arguments:
+
+ AccountHandle - The handle to the Account object whose system access
+ flags are to be read. This handle will have been returned
+ from a preceding LsaOpenAccount() or LsaCreateAccount() call
+ an must be open for ACCOUNT_VIEW access.
+
+ SystemAccess - Points to location that will receive the system access
+ flags for the account.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call was successful.
+
+ STATUS_ACCESS_DENIED - The AccountHandle does not specify
+ ACCOUNT_VIEW access.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is invalid.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAPR_ACCOUNT_INFO AccountInformation = NULL;
+
+ Status = LsarQueryInformationAccount(
+ AccountHandle,
+ AccountSystemAccessInformation,
+ &AccountInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GetSystemAccessAccountError;
+ }
+
+ *SystemAccess = *((PULONG) AccountInformation);
+
+GetSystemAccessAccountFinish:
+
+ //
+ // If necessary, free the buffer in which the Account Information was
+ // returned.
+ //
+
+ if (AccountInformation != NULL) {
+
+ MIDL_user_free( AccountInformation );
+ AccountInformation = NULL;
+ }
+
+ return(Status);
+
+GetSystemAccessAccountError:
+
+ *SystemAccess = 0;
+ goto GetSystemAccessAccountFinish;
+}
+
+
+NTSTATUS
+LsarSetSystemAccessAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN ULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetSystemAccessAccount() service sets the System Access
+ account flags for an Account object.
+
+Arguments:
+
+ AccountHandle - The handle to the Account object whose system access
+ flags are to be read. This handle will have been returned
+ from a preceding LsaOpenAccount() or LsaCreateAccount() call
+ an must be open for ACCOUNT_ADJUST_SYSTEM_ACCESS access.
+
+ SystemAccess - A mask of the system access flags to assign to the
+ Account object. The valid access flags include:
+
+ POLICY_MODE_INTERACTIVE - Account can be accessed interactively
+
+ POLICY_MODE_NETWORK - Account can be accessed remotely
+
+ POLICY_MODE_SERVICE - TBS
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call was successful.
+
+ STATUS_ACCESS_DENIED - The AccountHandle does not specify
+ ACCOUNT_VIEW access.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is invalid.
+
+ STATUS_INVALID_PARAMETER - The specified Access Flags are invalid.
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ BOOLEAN ObjectReferenced = FALSE;
+ PLSAPR_SID AccountSid = NULL;
+
+ //
+ // Verify that the specified flags are valid
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (SystemAccess != (SystemAccess & (POLICY_MODE_ALL))) {
+
+ goto SetSystemAccessAccountError;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the Account Object handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle and open a database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ AccountHandle,
+ ACCOUNT_ADJUST_SYSTEM_ACCESS,
+ AccountObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSystemAccessAccountError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Write the System Access flags
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ AccountHandle,
+ &LsapDbNames[ActSysAc],
+ &SystemAccess,
+ sizeof (ULONG)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSystemAccessAccountError;
+ }
+
+ //
+ // Update the Account Object Cache while holding the Lsa Database Lock.
+ // If the commit to backing storage below fails, caching will automatically
+ // be turned off.
+ //
+
+ AccountSid = LsapDbSidFromHandle( AccountHandle );
+
+ IgnoreStatus = LsapDbUpdateSystemAccessAccount(
+ AccountSid,
+ &SystemAccess
+ );
+
+SetSystemAccessAccountFinish:
+
+ //
+ // If necessary, dereference the Account object, close the database
+ // transaction, notify the LSA Database Replicator of the change,
+ // release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &AccountHandle,
+ AccountObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbChange,
+ Status
+ );
+ }
+
+ return(Status);
+
+SetSystemAccessAccountError:
+
+ goto SetSystemAccessAccountFinish;
+}
+
+
+NTSTATUS
+LsarQueryInformationAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN ACCOUNT_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_ACCOUNT_INFO *AccountInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ (as yet non-existent) LsarQueryInformationAccount API. Currently,
+ LsarGet...Account() API call this routine. In the future, this
+ routine may be added as an API.
+
+ The LsaQueryInformationAccount API obtains information from the Policy
+ object. The caller must have access appropriate to the information
+ being requested (see InformationClass parameter).
+
+Arguments:
+
+ AccountHandle - Handle from an LsaOpenAccount call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ AccountPrivilegeInformation ACCOUNT_VIEW
+ AccountQuotaInformation ACCOUNT_VIEW
+ AccountSystemAccessInformation ACCOUNT_VIEW
+
+ AccountInformation - Receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The Policy Database is possibly
+ corrupt. The returned Policy Information is invalid for
+ the given class.
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_DB_ACCOUNT Account = NULL;
+ PLSAPR_ACCOUNT_INFO CachedAccountInformation = NULL;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the Account Object handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ AccountHandle,
+ ACCOUNT_VIEW,
+ AccountObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+
+ if (LsapDbIsCacheValid(AccountObject)) {
+
+ Status = LsapDbQueryInformationAccount(
+ AccountHandle,
+ InformationClass,
+ AccountInformation
+ );
+
+ } else {
+
+ Status = LsapDbSlowQueryInformationAccount(
+ AccountHandle,
+ InformationClass,
+ AccountInformation
+ );
+ }
+
+ Status = LsapDbDereferenceObject(
+ &AccountHandle,
+ AccountObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsapDbQueryInformationAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN ACCOUNT_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_ACCOUNT_INFO *AccountInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the fast LSA server RPC worker routine for the
+ (as yet non-existent) LsarQueryInformationAccount API. It is called
+ when the in-memory Account List is valid.
+
+Arguments:
+
+ AccountHandle - Handle from an LsaOpenAccount call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ AccountPrivilegeInformation ACCOUNT_VIEW
+ AccountQuotaInformation ACCOUNT_VIEW
+ AccountSystemAccessInformation ACCOUNT_VIEW
+
+ AccountInformation - Receives a pointer to the buffer returned containing
+ the requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The Policy Database is possibly
+ corrupt. The returned Policy Information is invalid for
+ the given class.
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_DB_ACCOUNT Account = NULL;
+ ULONG AccountInformationLength;
+ PLSAPR_ACCOUNT_INFO CachedAccountInformation = NULL;
+ ULONG PrivilegesCount;
+ PLSAPR_PRIVILEGE_SET OutputPrivilegeSet = NULL;
+
+
+ (*AccountInformation) = NULL;
+
+ //
+ // Lookup the Account.
+ //
+
+ Status = LsapDbLookupAccount(
+ LsapDbSidFromHandle( AccountHandle ),
+ &Account
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInformationAccountError;
+ }
+
+ //
+ // Branch on Information Class.
+ //
+
+ switch (InformationClass) {
+
+ case AccountPrivilegeInformation:
+
+ //
+ // Calculate size of buffer needed for the output privilege set.
+ //
+
+ PrivilegesCount = 0;
+
+ if (Account->Info.PrivilegeSet != NULL) {
+
+ PrivilegesCount = Account->Info.PrivilegeSet->PrivilegeCount;
+ }
+
+ AccountInformationLength = sizeof(PRIVILEGE_SET) +
+ (PrivilegesCount * sizeof(LUID_AND_ATTRIBUTES)) -
+ (sizeof(LUID_AND_ATTRIBUTES));
+
+ CachedAccountInformation = (PLSAPR_ACCOUNT_INFO) Account->Info.PrivilegeSet;
+ break;
+
+ case AccountQuotaInformation:
+
+ //
+ // Calculate size of buffer needed for the output privilege set.
+ //
+
+ AccountInformationLength = sizeof(QUOTA_LIMITS);
+ CachedAccountInformation = (PLSAPR_ACCOUNT_INFO) &Account->Info.QuotaLimits;
+ break;
+
+ case AccountSystemAccessInformation:
+
+ //
+ // Calculate size of buffer needed for the output privilege set.
+ //
+
+ AccountInformationLength = sizeof(ULONG);
+ CachedAccountInformation = (PLSAPR_ACCOUNT_INFO) &Account->Info.SystemAccess;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInformationAccountError;
+ }
+
+ //
+ // Allocate output buffer.
+ //
+
+ *AccountInformation = MIDL_user_allocate( AccountInformationLength );
+
+ Status = STATUS_NO_MEMORY;
+
+ if (*AccountInformation == NULL) {
+
+ goto QueryInformationAccountError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Copy the data requested if the cached information is non-NULL.
+ //
+
+ if (CachedAccountInformation != NULL) {
+
+ RtlCopyMemory(
+ *AccountInformation,
+ CachedAccountInformation,
+ AccountInformationLength
+ );
+
+ goto QueryInformationAccountFinish;
+ }
+
+ //
+ // The cached information is NULL. The only information class for which
+ // this can happen is AccountPrivilegeInformation, since this is the
+ // only class for which a pointer is kept rather than in-structure data.
+ //
+
+ if (InformationClass == AccountPrivilegeInformation) {
+
+ OutputPrivilegeSet = (PLSAPR_PRIVILEGE_SET) *AccountInformation;
+ OutputPrivilegeSet->PrivilegeCount = 0;
+ OutputPrivilegeSet->Control = 0;
+
+ } else {
+
+ Status = STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInformationAccountError;
+ }
+
+QueryInformationAccountFinish:
+
+ return(Status);
+
+QueryInformationAccountError:
+
+ if (*AccountInformation) {
+ MIDL_user_free(*AccountInformation);
+ }
+ (*AccountInformation) = NULL;
+ goto QueryInformationAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbSlowQueryInformationAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ IN ACCOUNT_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_ACCOUNT_INFO *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the slow LSA server RPC worker routine for the
+ (as yet non-existent) LsarQueryInformationAccount API. It is called
+ when the in-memory Account List is valid.
+
+Arguments:
+
+ AccountHandle - Handle from an LsaOpenAccount call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ AccountPrivilegeInformation ACCOUNT_VIEW
+ AccountQuotaInformation ACCOUNT_VIEW
+ AccountSystemAccessInformation ACCOUNT_VIEW
+
+ Buffer - Receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The Policy Database is possibly
+ corrupt. The returned Policy Information is invalid for
+ the given class.
+--*/
+
+{
+ NTSTATUS Status;
+ QUOTA_LIMITS QuotaLimits;
+ ULONG SystemAccess;
+ PLSAPR_ACCOUNT_INFO OutputBuffer = NULL;
+
+ //
+ // Branch on Information Class.
+ //
+
+ switch (InformationClass) {
+
+ case AccountPrivilegeInformation:
+
+ Status = LsapDbSlowQueryPrivilegesAccount(
+ AccountHandle,
+ (PLSAPR_PRIVILEGE_SET *) &OutputBuffer
+ );
+ break;
+
+ case AccountQuotaInformation:
+
+ Status = LsapDbSlowQueryQuotasAccount(
+ AccountHandle,
+ &QuotaLimits
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ OutputBuffer = MIDL_user_allocate( sizeof(QUOTA_LIMITS));
+
+ Status = STATUS_NO_MEMORY;
+
+ if (OutputBuffer == NULL) {
+
+ break;
+ }
+
+ *((PQUOTA_LIMITS) OutputBuffer) = QuotaLimits;
+ Status = STATUS_SUCCESS;
+ break;
+
+ case AccountSystemAccessInformation:
+
+ Status = LsapDbSlowQuerySystemAccessAccount(
+ AccountHandle,
+ &SystemAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ OutputBuffer = MIDL_user_allocate( sizeof(ULONG));
+
+ Status = STATUS_NO_MEMORY;
+
+ if (OutputBuffer == NULL) {
+
+ break;
+ }
+
+ *((PULONG) OutputBuffer) = SystemAccess;
+ Status = STATUS_SUCCESS;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryInformationAccountError;
+ }
+
+ *Buffer = OutputBuffer;
+
+SlowQueryInformationAccountFinish:
+
+ return(Status);
+
+SlowQueryInformationAccountError:
+
+ *Buffer = NULL;
+ goto SlowQueryInformationAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbQueryAllInformationAccounts(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PLSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO AccountInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine:
+
+ 1) Gets all privileges assigned to the user (or any group/alias
+ the user is a member of).
+
+ 2) Establishes the quotas assigned to the user. This is the
+ maximum of the system default quotas or any quotas assigned
+ to the user (or any group/alias the user is a member of).
+
+ 3) Gets all the System Accesses assigned to the user.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call. The handle must
+ have POLICY_VIEW_LOCAL_INFORMATION access granted.
+
+ IdCount - Indicates the number of IDs being provided in the Ids array.
+
+ Ids - Points to an array of SIDs.
+
+ AccountInfo - Pointer to buffer that will receive the Account information
+ comprising its Privilege Set, System Access Flags and Quotas.
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_LOGON_TYPE_NOT_GRANTED - Indicates the specified type of logon
+ has not been granted to any of the IDs in the passed set.
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN ObjectReferenced = FALSE;
+ PPRIVILEGE_SET RunningPrivileges = NULL;
+ PPRIVILEGE_SET UpdatedRunningPrivileges = NULL;
+ PPRIVILEGE_SET NextPrivileges = NULL;
+ ULONG RunningPrivilegesSize, SizeNeeded;
+ ULONG RunningSystemAccess;
+ QUOTA_LIMITS NextQuotaLimits;
+ QUOTA_LIMITS RunningQuotaLimits;
+ PQUOTA_LIMITS PolicyDefaultQuotaLimits = NULL;
+ PPOLICY_DEFAULT_QUOTA_INFO PolicyDefaultQuotaInfo = NULL;
+ ULONG SidIndex;
+ PLSAP_DB_ACCOUNT Account = NULL;
+
+ //
+ // If we are unable to use the Account List, use the slow method
+ // for querying Privileges and Quotas
+ //
+
+ if (!LsapDbIsCacheValid(AccountObject)) {
+
+ return(LsapDbSlowQueryAllInformationAccounts(
+ PolicyHandle,
+ IdCount,
+ Ids,
+ AccountInfo
+ ));
+ }
+
+ //
+ // The Account List is valid. We'll use it instead of opening individual
+ // Account objects. Verify that the Policy Handle is valid, is the handle
+ // object and has the necessary access granted. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryAllInformationAccountsError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Obtain the Master Default Quota Limits from the Policy Object. If
+ // the Policy Object is cached (partially for now) instruct the query
+ // routine to just copy the data.
+
+ PolicyDefaultQuotaLimits = &RunningQuotaLimits;
+
+ Status = LsapDbQueryInformationPolicy(
+ PolicyHandle,
+ PolicyDefaultQuotaInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyDefaultQuotaLimits
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryAllInformationAccountsError;
+ }
+
+ //
+ // Iterate through all of the Sids provided. For each one, check if the
+ // Sid is that of an Account object in the local LSA. If it is,
+ //
+ // (1) Obtain the System Accesses and add those found so far.
+ // (2) Obtain the Account Privileges and add to those found so far.
+ // (3) Obtain the Quota Limits (if any) assigned to the account.
+ // Compare these with the quota limits obtained so far. If any
+ // limits are more generous than the running values, update
+ // the running values.
+ //
+
+ RunningSystemAccess = 0;
+
+ for( SidIndex = 0; SidIndex < IdCount; SidIndex++) {
+
+ //
+ // Locate the Account information block for this Sid.
+ //
+
+ Status = LsapDbLookupAccount( Ids[SidIndex].Sid, &Account );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status == STATUS_NO_SUCH_USER) {
+
+ Status = STATUS_SUCCESS;
+ continue;
+ }
+
+ break;
+ }
+
+ //
+ // We have found the Account information. Add in the System Accesses
+ // for this account.
+ //
+
+ RunningSystemAccess |= Account->Info.SystemAccess;
+
+ //
+ // Obtain the account's Special privileges.
+ //
+
+ NextPrivileges = Account->Info.PrivilegeSet;
+
+ //
+ // Add the Privileges of this account (if any) to the running set.
+ //
+
+ if (NextPrivileges != NULL) {
+
+ SizeNeeded = 0;
+
+ Status = LsapRtlAddPrivileges(
+ (PPRIVILEGE_SET) RunningPrivileges,
+ (PPRIVILEGE_SET) NextPrivileges,
+ NULL,
+ &SizeNeeded,
+ RTL_COMBINE_PRIVILEGE_ATTRIBUTES
+ );
+
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+
+ goto QueryAllInformationAccountsError;
+ }
+
+ //
+ // Now allocate buffer for the updated Privilege Set (if non NULL).
+ //
+
+ UpdatedRunningPrivileges = MIDL_user_allocate( SizeNeeded );
+
+ Status = STATUS_NO_MEMORY;
+
+ if (UpdatedRunningPrivileges == NULL) {
+
+ goto QueryAllInformationAccountsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Now generate the updated privilege set.
+ //
+
+ RunningPrivilegesSize = SizeNeeded;
+
+ Status = LsapRtlAddPrivileges(
+ (PPRIVILEGE_SET) RunningPrivileges,
+ (PPRIVILEGE_SET) NextPrivileges,
+ (PPRIVILEGE_SET) UpdatedRunningPrivileges,
+ &SizeNeeded,
+ RTL_COMBINE_PRIVILEGE_ATTRIBUTES
+ );
+ ASSERT(RunningPrivilegesSize == SizeNeeded);
+
+ if (!NT_SUCCESS(Status)) {
+
+ MIDL_user_free(UpdatedRunningPrivileges);
+ goto QueryAllInformationAccountsError;
+ }
+
+ //
+ // Free the old running privileges buffer and update pointer.
+ //
+
+ if (RunningPrivileges != NULL) {
+
+ MIDL_user_free( RunningPrivileges );
+ }
+
+ RunningPrivileges = UpdatedRunningPrivileges;
+ UpdatedRunningPrivileges = NULL;
+ }
+
+ //
+ // Obtain the special Quota Limits for this account (if any).
+ //
+
+ RtlMoveMemory(&NextQuotaLimits, &Account->Info.QuotaLimits, sizeof(QUOTA_LIMITS));
+
+ //
+ // Special Quota Limits are assigned. Compare each of the quota
+ // limits obtained with the running values. If a quota limit just
+ // obtained is less restrictive than the running value, supersede the
+ // running value.
+ //
+
+ if (RunningQuotaLimits.PagedPoolLimit < NextQuotaLimits.PagedPoolLimit) {
+
+ RunningQuotaLimits.PagedPoolLimit = NextQuotaLimits.PagedPoolLimit;
+ }
+
+ if (RunningQuotaLimits.NonPagedPoolLimit < NextQuotaLimits.NonPagedPoolLimit) {
+
+ RunningQuotaLimits.NonPagedPoolLimit = NextQuotaLimits.NonPagedPoolLimit;
+ }
+
+ if (RunningQuotaLimits.MinimumWorkingSetSize > NextQuotaLimits.MinimumWorkingSetSize) {
+
+ RunningQuotaLimits.MinimumWorkingSetSize = NextQuotaLimits.MinimumWorkingSetSize;
+ }
+
+ if (RunningQuotaLimits.MaximumWorkingSetSize < NextQuotaLimits.MaximumWorkingSetSize) {
+
+ RunningQuotaLimits.MaximumWorkingSetSize = NextQuotaLimits.MaximumWorkingSetSize;
+ }
+
+ if (RunningQuotaLimits.PagefileLimit < NextQuotaLimits.PagefileLimit) {
+
+ RunningQuotaLimits.PagefileLimit = NextQuotaLimits.PagefileLimit;
+ }
+
+ if (RunningQuotaLimits.TimeLimit.QuadPart < NextQuotaLimits.TimeLimit.QuadPart) {
+
+ RunningQuotaLimits.TimeLimit = NextQuotaLimits.TimeLimit;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryAllInformationAccountsError;
+ }
+
+ //
+ // Return the collective Privilege Set
+ //
+
+ AccountInfo->PrivilegeSet = RunningPrivileges;
+
+ //
+ // Return the collective System Accesses
+
+ AccountInfo->SystemAccess = RunningSystemAccess;
+
+ //
+ // Return the collective Quota Limits
+ //
+
+ AccountInfo->QuotaLimits = RunningQuotaLimits;
+
+QueryAllInformationAccountsFinish:
+
+ //
+ // If necessary, dereference the Policy Object.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+QueryAllInformationAccountsError:
+
+ //
+ // If necessary, free the memory allocated for the Privilege Set.
+ //
+
+ if (RunningPrivileges != NULL) {
+
+ MIDL_user_free( RunningPrivileges );
+ RunningPrivileges = NULL;
+ }
+
+
+
+ //
+ // Return null values
+ //
+
+ RtlZeroMemory( &AccountInfo->QuotaLimits, sizeof(QUOTA_LIMITS) );
+ AccountInfo->SystemAccess = 0;
+ AccountInfo->PrivilegeSet = NULL;
+ goto QueryAllInformationAccountsFinish;
+}
+
+
+NTSTATUS
+LsapDbSlowQueryAllInformationAccounts(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PLSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO AccountInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the slow version of LsapDbQueryInformation().
+ It is called when the Account List is not available, and assembles the
+ necessary information from the Policy Database.
+
+ This routine:
+
+ 1) Gets all privileges assigned to the user (or any group/alias
+ the user is a member of).
+
+ 2) Establishes the quotas assigned to the user. This is the
+ maximum of the system default quotas or any quotas assigned
+ to the user (or any group/alias the user is a member of).
+
+ 3) Gets all the System Accesses assigned to the user.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call. The handle must
+ have POLICY_VIEW_LOCAL_INFORMATION access granted.
+
+ IdCount - Indicates the number of IDs being provided in the Ids array.
+
+ Ids - Points to an array of SIDs.
+
+ AccountInfo - Pointer to buffer that will receive the Account information
+ comprising its Privilege Set, System Access Flags and Quotas.
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_LOGON_TYPE_NOT_GRANTED - Indicates the specified type of logon
+ has not been granted to any of the IDs in the passed set.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS LocalStatus;
+ BOOLEAN ObjectReferenced = FALSE;
+ PPRIVILEGE_SET RunningPrivileges = NULL;
+ PPRIVILEGE_SET UpdatedRunningPrivileges = NULL;
+ PPRIVILEGE_SET NextPrivileges = NULL;
+ ULONG RunningPrivilegesSize, SizeNeeded;
+ ULONG RunningSystemAccess;
+ QUOTA_LIMITS NextQuotaLimits;
+ QUOTA_LIMITS RunningQuotaLimits;
+ PQUOTA_LIMITS PointerToNextQuotaLimits = NULL;
+ PQUOTA_LIMITS PolicyDefaultQuotaLimits = NULL;
+ ULONG SidIndex;
+ LSAPR_HANDLE AccountHandle = NULL;
+ PULONG SystemAccessThisId = NULL;
+
+ //
+ // Verify that the Policy Handle is valid, is the handle to the Policy
+ // object and has the necessary access granted. Reference the handle.
+ // Note that the Lsa Database lock is NOT held at this point. Instead,
+ // the lock is taken and released by called routines where required.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ PolicyDefaultQuotaLimits = &RunningQuotaLimits;
+
+ //
+ // Obtain the Master Default Quota Limits from the Policy Object.
+ //
+
+ Status = LsapDbQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyDefaultQuotaInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyDefaultQuotaLimits
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ //
+ // Iterate through all of the Sids provided. For each one, check if the
+ // Sid is that of an Account object in the local LSA. If it is,
+ //
+ // (1) Obtain the System Accesses and add those found so far.
+ // (2) Obtain the Account Privileges and add to thosde found so far.
+ // (3) Obtain the Quota Limits (if any) assigned to the account.
+ // Compare these with the quota limits obtained so far. If any
+ // limits are more generous than the running values, update
+ // the running values.
+ //
+
+ RunningSystemAccess = 0;
+
+ for( SidIndex = 0; SidIndex < IdCount; SidIndex++) {
+
+ //
+ // Attempt to open an Lsa Account object specifying the next Sid.
+ // If successful, the open returns a Trusted handle to the account.
+ //
+
+ Status = LsarOpenAccount(
+ PolicyHandle,
+ Ids[SidIndex].Sid,
+ ACCOUNT_VIEW,
+ (LSAPR_HANDLE *) &AccountHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+ continue;
+ }
+
+ //
+ // An account object has been successfully opened. Obtain
+ // its system accesses.
+ //
+
+ Status = LsapDbSlowQueryInformationAccount(
+ AccountHandle,
+ AccountSystemAccessInformation,
+ (PLSAPR_ACCOUNT_INFO *) &SystemAccessThisId
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ RunningSystemAccess |= *SystemAccessThisId;
+
+ //
+ // Obtain the account's Special privileges.
+ //
+
+ NextPrivileges = NULL;
+
+ Status = LsapDbSlowQueryInformationAccount(
+ AccountHandle,
+ AccountPrivilegeInformation,
+ (PLSAPR_ACCOUNT_INFO *) &NextPrivileges
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ //
+ // Add the Privileges of this account (if any) to the running set.
+ //
+
+ if (NextPrivileges != NULL) {
+
+ SizeNeeded = 0;
+ Status = LsapRtlAddPrivileges(
+ (PPRIVILEGE_SET) RunningPrivileges,
+ (PPRIVILEGE_SET) NextPrivileges,
+ NULL,
+ &SizeNeeded,
+ RTL_COMBINE_PRIVILEGE_ATTRIBUTES
+ );
+
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ //
+ // Now allocate buffer for the updated privilege set (if non NULL).
+ //
+
+ UpdatedRunningPrivileges = MIDL_user_allocate( SizeNeeded );
+
+ if (UpdatedRunningPrivileges == NULL) {
+
+ Status = STATUS_NO_MEMORY;
+ MIDL_user_free( NextPrivileges );
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ //
+ // Now generate the updated privilege set.
+ //
+
+ RunningPrivilegesSize = SizeNeeded;
+ Status = LsapRtlAddPrivileges(
+ (PPRIVILEGE_SET) RunningPrivileges,
+ (PPRIVILEGE_SET) NextPrivileges,
+ (PPRIVILEGE_SET) UpdatedRunningPrivileges,
+ &SizeNeeded,
+ RTL_COMBINE_PRIVILEGE_ATTRIBUTES
+ );
+ ASSERT(RunningPrivilegesSize == SizeNeeded);
+ MIDL_user_free( NextPrivileges );
+
+ if (!NT_SUCCESS(Status)) {
+
+ MIDL_user_free(UpdatedRunningPrivileges);
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ //
+ // Free the old running privileges buffer and update pointer.
+ //
+
+ if (RunningPrivileges != NULL) {
+
+ MIDL_user_free( RunningPrivileges );
+ }
+
+ RunningPrivileges = UpdatedRunningPrivileges;
+ UpdatedRunningPrivileges = NULL;
+ }
+
+ //
+ // Obtain the special Quota Limits for this account (if any).
+ //
+
+ Status = LsapDbSlowQueryInformationAccount(
+ AccountHandle,
+ AccountQuotaInformation,
+ (PLSAPR_ACCOUNT_INFO *) &PointerToNextQuotaLimits
+ );
+
+ if (Status == STATUS_NO_QUOTAS_FOR_ACCOUNT) {
+
+ continue;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ NextQuotaLimits = *PointerToNextQuotaLimits;
+
+ //
+ // Special Quota Limits are assigned. Compare each of the quota
+ // limits obtained with the running values. If a quota limit just
+ // obtained is less restrictive than the running value, supersede the
+ // running value.
+ //
+
+ if (RunningQuotaLimits.PagedPoolLimit < NextQuotaLimits.PagedPoolLimit) {
+
+ RunningQuotaLimits.PagedPoolLimit = NextQuotaLimits.PagedPoolLimit;
+ }
+
+ if (RunningQuotaLimits.NonPagedPoolLimit < NextQuotaLimits.NonPagedPoolLimit) {
+
+ RunningQuotaLimits.NonPagedPoolLimit = NextQuotaLimits.NonPagedPoolLimit;
+ }
+
+ if (RunningQuotaLimits.MinimumWorkingSetSize > NextQuotaLimits.MinimumWorkingSetSize) {
+
+ RunningQuotaLimits.MinimumWorkingSetSize = NextQuotaLimits.MinimumWorkingSetSize;
+ }
+
+ if (RunningQuotaLimits.MaximumWorkingSetSize < NextQuotaLimits.MaximumWorkingSetSize) {
+
+ RunningQuotaLimits.MaximumWorkingSetSize = NextQuotaLimits.MaximumWorkingSetSize;
+ }
+
+ if (RunningQuotaLimits.PagefileLimit < NextQuotaLimits.PagefileLimit) {
+
+ RunningQuotaLimits.PagefileLimit = NextQuotaLimits.PagefileLimit;
+ }
+
+ if (RunningQuotaLimits.TimeLimit.QuadPart < NextQuotaLimits.TimeLimit.QuadPart) {
+
+ RunningQuotaLimits.TimeLimit = NextQuotaLimits.TimeLimit;
+ }
+
+ //
+ // Close the account handle
+ //
+
+ LocalStatus = LsarClose( &AccountHandle );
+ ASSERT(NT_SUCCESS(LocalStatus));
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryAllInformationAccountsError;
+ }
+
+ //
+ // Return the collective Privilege Set
+ //
+
+ AccountInfo->PrivilegeSet = RunningPrivileges;
+
+ //
+ // Return the collective System Accesses
+
+ AccountInfo->SystemAccess = RunningSystemAccess;
+
+ //
+ // Return the collective Quota Limits
+ //
+
+ AccountInfo->QuotaLimits = RunningQuotaLimits;
+
+SlowQueryAllInformationAccountsFinish:
+
+ //
+ // If necessary, dereference the Policy Object.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+SlowQueryAllInformationAccountsError:
+
+ //
+ // If necessary, free the memory allocated for the Privilege Set.
+ //
+
+ if (RunningPrivileges != NULL) {
+
+ MIDL_user_free( RunningPrivileges );
+ RunningPrivileges = NULL;
+ }
+
+ //
+ // Close an account handle, if one is open
+ //
+
+ if (AccountHandle != NULL) {
+
+ LocalStatus = LsarClose( &AccountHandle );
+ ASSERT(NT_SUCCESS(LocalStatus));
+ }
+
+ //
+ // Return null values
+ //
+
+ RtlZeroMemory( &AccountInfo->QuotaLimits, sizeof(QUOTA_LIMITS) );
+ AccountInfo->SystemAccess = 0;
+ AccountInfo->PrivilegeSet = NULL;
+ goto SlowQueryAllInformationAccountsFinish;
+}
+
+
+
+NTSTATUS
+LsapDbSlowQueryPrivilegesAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PLSAPR_PRIVILEGE_SET *Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the slow LSA server RPC worker routine for the
+ LsaEnumeratePrivilegesOfAccount API.
+
+ The LsaEnumeratePrivilegesOfAccount API obtains information which
+ describes the privileges assigned to an account. This call requires
+ ACCOUNT_VIEW access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose privilege
+ information is to be obtained. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ Privileges - Receives a pointer to a buffer containing the Privilege
+ Set. The Privilege Set is an array of structures, one for each
+ privilege. Each structure contains the LUID of the privilege and
+ a mask of the privilege's attributes.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PPRIVILEGE_SET PrivilegeSet = NULL;
+ ULONG PrivilegeSetLength;
+ BOOLEAN ObjectReferenced = FALSE;
+
+ //
+ // QueryPrivileges the size of the buffer required for the Privilege Set.
+ // The Privilege Set is the value of the Privileg attribute of the
+ // account object.
+ //
+
+ PrivilegeSetLength = (ULONG) 0;
+
+ Status = LsapDbReadAttributeObject(
+ AccountHandle,
+ &LsapDbNames[Privilgs],
+ NULL,
+ &PrivilegeSetLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If the Privileg attribute does not exist, convert the status
+ // back to STATUS_SUCCESS. Note that an account object need not
+ // have any privileges assigned so STATUS_OBJECT_NAME_NOT_FOUND is
+ // not an error in this case. Return a Privilege Set containing
+ // a zero Count.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto SlowQueryPrivilegesError;
+ }
+
+ PrivilegeSetLength = sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES);
+
+ PrivilegeSet = MIDL_user_allocate ( PrivilegeSetLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PrivilegeSet == NULL) {
+
+ goto SlowQueryPrivilegesError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ PrivilegeSet->Control = (ULONG) 0L;
+ PrivilegeSet->PrivilegeCount = (ULONG) 0L;
+ *Privileges = (PLSAPR_PRIVILEGE_SET) PrivilegeSet;
+ goto SlowQueryPrivilegesFinish;
+
+ } else if (PrivilegeSetLength <= sizeof(PRIVILEGE_SET) - sizeof (LUID_AND_ATTRIBUTES)) {
+
+ //
+ // The privilege set attribute exists but has zero entries.
+
+ PrivilegeSet = MIDL_user_allocate ( PrivilegeSetLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PrivilegeSet == NULL) {
+
+ goto SlowQueryPrivilegesError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ PrivilegeSet->Control = (ULONG) 0L;
+ PrivilegeSet->PrivilegeCount = (ULONG) 0L;
+ *Privileges = (PLSAPR_PRIVILEGE_SET) PrivilegeSet;
+ goto SlowQueryPrivilegesFinish;
+ }
+
+ //
+ // The Privileg attribute exists and has a value assigned. Allocate
+ // a buffer for its value.
+ //
+
+ PrivilegeSet = MIDL_user_allocate ( PrivilegeSetLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PrivilegeSet == NULL) {
+
+ goto SlowQueryPrivilegesError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Read the Privilgs attribute into the buffer. Note that although
+ // the value of this attribute has a variable length, it is bounded
+ // above.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ AccountHandle,
+ &LsapDbNames[Privilgs],
+ PrivilegeSet,
+ &PrivilegeSetLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ MIDL_user_free(PrivilegeSet);
+ goto SlowQueryPrivilegesError;
+ }
+
+ //
+ // Return the Privilege Set or NULL
+ //
+
+ *Privileges = (PLSAPR_PRIVILEGE_SET) PrivilegeSet;
+
+SlowQueryPrivilegesFinish:
+
+ return( Status );
+
+SlowQueryPrivilegesError:
+
+ *Privileges = NULL;
+ goto SlowQueryPrivilegesFinish;
+}
+
+
+NTSTATUS
+LsapDbSlowQueryQuotasAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PQUOTA_LIMITS QuotaLimits
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the slow LSA server RPC worker routine for the
+ LsarGetQuotasForAccount API.
+
+ The LsaGetQuotasForAccount API obtains the quota limits for pageable and
+ non-pageable memory (in Kilobytes) and the maximum execution time (in
+ seconds) for any session logged on to the account specified by
+ AccountHandle. For each quota and explicit value is returned. This
+ call requires LSA_ACCOUNT_VIEW access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose quotas
+ are to be obtained. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ QuotaLimits - Pointer to structure in which the system resource
+ quota limits applicable to each session logged on to this account
+ will be returned. Note that all quotas, including those specified
+ as being the system default values, are returned as actual values.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG QuotaLimitsLength = sizeof (QUOTA_LIMITS);
+
+ //
+ // Read the Account Object's Quotas from the LSA Database
+ //
+
+ Status = LsapDbReadAttributeObject(
+ AccountHandle,
+ &LsapDbNames[QuotaLim],
+ QuotaLimits,
+ &QuotaLimitsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If there is no QuotaLim attribute, return the system default
+ // quotas.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto SlowQueryQuotasError;
+ }
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbContainerFromHandle(AccountHandle),
+ &LsapDbNames[DefQuota],
+ QuotaLimits,
+ &QuotaLimitsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryQuotasError;
+ }
+ }
+
+SlowQueryQuotasFinish:
+
+ return Status;
+
+SlowQueryQuotasError:
+
+ goto SlowQueryQuotasFinish;
+}
+
+
+NTSTATUS
+LsapDbSlowQuerySystemAccessAccount(
+ IN LSAPR_HANDLE AccountHandle,
+ OUT PULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the Slow worker for the LsaGetSystemAccessAccount()
+ API.
+
+Arguments:
+
+ AccountHandle - The handle to the Account object whose system access
+ flags are to be read. This handle will have been returned
+ from a preceding LsaOpenAccount() or LsaCreateAccount() call
+ an must be open for ACCOUNT_VIEW access.
+
+ SystemAccess - Points to location that will receive the system access
+ flags for the account.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call was successful.
+
+ STATUS_ACCESS_DENIED - The AccountHandle does not specify
+ ACCOUNT_VIEW access.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is invalid.
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG ReturnedSystemAccess;
+ ULONG ReturnedSystemAccessLength;
+
+ //
+ // Read the Account Object's System Access Flags
+ //
+
+ ReturnedSystemAccessLength = sizeof(ULONG);
+
+ Status = LsapDbReadAttributeObject(
+ AccountHandle,
+ &LsapDbNames[ActSysAc],
+ &ReturnedSystemAccess,
+ &ReturnedSystemAccessLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If there is no System Access attribute, return the system default
+ // access.
+ //
+ // NOTE: The Master Default for the System Access attribute is
+ // currently hardwired.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto SlowQuerySystemAccessAccountError;
+ }
+
+ ReturnedSystemAccess = LSAP_DB_ACCOUNT_DEFAULT_SYS_ACCESS;
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // Verify that the returned flags are valid
+ //
+
+ if (ReturnedSystemAccess != (ReturnedSystemAccess & POLICY_MODE_ALL)) {
+
+ Status = STATUS_INTERNAL_DB_CORRUPTION;
+ goto SlowQuerySystemAccessAccountError;
+ }
+ }
+
+ *SystemAccess = ReturnedSystemAccess;
+
+SlowQuerySystemAccessAccountFinish:
+
+ return(Status);
+
+SlowQuerySystemAccessAccountError:
+
+ *SystemAccess = 0;
+ goto SlowQuerySystemAccessAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupAccount(
+ IN PSID AccountSid,
+ OUT PLSAP_DB_ACCOUNT *Account
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up the Account information for a given Lsa Account.
+
+Arguments:
+
+ AccountSid - Sid of the account
+
+ Account - Receives a pointer to the Account information.
+
+--*/
+
+{
+ PLSAP_DB_ACCOUNT NextAccount = NULL;
+ ULONG AccountIndex;
+ BOOLEAN AccountFound = FALSE;
+
+ //
+ // Scan the list of Accounts.
+ //
+
+ for (AccountIndex = 0, NextAccount = LsapDbFirstAccount();
+ AccountIndex < LsapDbAccountList.AccountCount;
+ AccountIndex++, NextAccount = LsapDbNextAccount( NextAccount)
+ ) {
+
+ //
+ // If the Sids match, we've found the account.
+ //
+
+ if (RtlEqualSid( AccountSid, NextAccount->Sid )) {
+
+ *Account = NextAccount;
+ AccountFound = TRUE;
+ break;
+ }
+ }
+
+ if (AccountFound) {
+
+ return(STATUS_SUCCESS);
+ }
+
+ return(STATUS_NO_SUCH_USER);
+}
+
+
+NTSTATUS
+LsapDbCreateAccount(
+ IN PLSAPR_SID AccountSid,
+ OUT OPTIONAL PLSAP_DB_ACCOUNT *Account
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates an Account's information block
+
+Arguments:
+
+ AccountSid - Specifies the Sid of the Account
+
+ Account - Optionally receives a pointer to the newly created Account
+ information block.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAPR_SID CopiedSid = NULL;
+ PLSAP_DB_ACCOUNT OutputAccount = NULL;
+
+ //
+ // Verify that the Account List is valid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if ((!LsapDbIsCacheValid(AccountObject)) && LsapInitialized ) {
+
+ goto CreateAccountError;
+ }
+
+ //
+ // Make a copy of the Sid.
+ //
+
+ Status = LsapRpcCopySid(
+ NULL,
+ (PSID *) &CopiedSid,
+ (PSID) AccountSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateAccountError;
+ }
+
+ //
+ // Allocate memory for the Account information block.
+ //
+
+ OutputAccount = MIDL_user_allocate( sizeof(LSAP_DB_ACCOUNT) );
+
+ Status = STATUS_NO_MEMORY;
+
+ if (OutputAccount == NULL) {
+
+ goto CreateAccountError;
+ }
+
+ //
+ // Zeroise the new block.
+ //
+
+ RtlZeroMemory( OutputAccount, sizeof(LSAP_DB_ACCOUNT) );
+
+ //
+ // Copy in the Sid.
+ //
+
+ OutputAccount->Sid = CopiedSid;
+
+ //
+ // Link the Account to the head of the Account List.
+ //
+
+ InsertHeadList( &LsapDbAccountList.Links, &OutputAccount->Links );
+
+ //
+ // If requested, return a pointer to the Account.
+ //
+
+ if (Account != NULL) {
+
+ *Account = OutputAccount;
+ }
+
+ LsapDbAccountList.AccountCount++;
+
+ Status = STATUS_SUCCESS;
+
+CreateAccountFinish:
+
+ return(Status);
+
+CreateAccountError:
+
+ //
+ // If necessary, free the copied Sid.
+ //
+
+ if (CopiedSid != NULL) {
+
+ MIDL_user_free( CopiedSid );
+ CopiedSid = NULL;
+ }
+
+ //
+ // If necessary, free the memory allocated for the Account block.
+ //
+
+ if (OutputAccount != NULL) {
+
+ MIDL_user_free( OutputAccount);
+ OutputAccount = NULL;
+ }
+
+ //
+ // If a return pointer was specified, return NULL.
+ //
+
+ if (Account != NULL) {
+
+ *Account = NULL;
+ }
+
+ goto CreateAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbDeleteAccount(
+ IN PLSAPR_SID AccountSid
+ )
+
+/*++
+
+Routine Description:
+
+ This function deletes an Account's information block
+
+Arguments:
+
+ AccountSid - Specifies the Sid of the Account
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_DB_ACCOUNT Account = NULL;
+
+ //
+ // Verify that the Account List is valid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!LsapDbIsCacheValid(AccountObject)) {
+
+ goto DeleteAccountError;
+ }
+
+ //
+ // Lookup the Account Information Block to be deleted
+ //
+
+ Status = LsapDbLookupAccount( AccountSid, &Account);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteAccountError;
+ }
+
+ //
+ // We found the account. Unlink it from the Account List
+ //
+
+ RemoveEntryList( &Account->Links );
+
+ //
+ // Now free the Account Information
+ //
+
+ if (Account->Sid != NULL) {
+
+ MIDL_user_free( Account->Sid);
+ Account->Sid = NULL;
+ }
+
+ if (Account->Info.PrivilegeSet != NULL) {
+
+ MIDL_user_free( Account->Info.PrivilegeSet );
+ Account->Info.PrivilegeSet = NULL;
+ }
+
+ MIDL_user_free( Account );
+
+ LsapDbAccountList.AccountCount--;
+
+DeleteAccountFinish:
+
+ return(Status);
+
+DeleteAccountError:
+
+ goto DeleteAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbUpdateSystemAccessAccount(
+ IN PLSAPR_SID AccountSid,
+ IN PULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function updates the System Access flags in an Account's information
+ block.
+
+Arguments:
+
+ AccountSid - Sid of account
+
+ SystemAccess - Pointer to new System Access flags. These flags
+ will overwrite the old value
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_DB_ACCOUNT Account = NULL;
+
+ //
+ // Verify that the Account List is valid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!LsapDbIsCacheValid(AccountObject)) {
+
+ goto UpdateSystemAccessAccountError;
+ }
+
+ //
+ // Lookup the Account
+ //
+
+ Status = LsapDbLookupAccount( AccountSid, &Account );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UpdateSystemAccessAccountError;
+ }
+
+ //
+ // Update the System Access Flags
+ //
+
+ Account->Info.SystemAccess = *SystemAccess;
+
+UpdateSystemAccessAccountFinish:
+
+ return(Status);
+
+UpdateSystemAccessAccountError:
+
+ goto UpdateSystemAccessAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbUpdateQuotasAccount(
+ IN PLSAPR_SID AccountSid,
+ IN PQUOTA_LIMITS QuotaLimits
+ )
+
+/*++
+
+Routine Description:
+
+ This function updates the Quota Limits an Account's information
+ block.
+
+Arguments:
+
+ AccountSid - Sid of Account
+
+ Quotas - Pointer to new Quota Limits flags. These flags
+ will overwrite the old value
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_DB_ACCOUNT Account = NULL;
+
+ //
+ // Verify that the Account List is valid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!LsapDbIsCacheValid(AccountObject)) {
+
+ goto UpdateQuotasAccountError;
+ }
+
+ //
+ // Lookup the Account
+ //
+
+ Status = LsapDbLookupAccount( AccountSid, &Account );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UpdateQuotasAccountError;
+ }
+
+ //
+ // Update the System Access Flags
+ //
+
+ Account->Info.QuotaLimits = *QuotaLimits;
+
+UpdateQuotasAccountFinish:
+
+ return(Status);
+
+UpdateQuotasAccountError:
+
+ goto UpdateQuotasAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbUpdatePrivilegesAccount(
+ IN PLSAPR_SID AccountSid,
+ IN OPTIONAL PPRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This function replates the Privilege Set in an Account's information
+ block with the one given. The existing Privilege Set (if any) in the
+ block will be freed.
+
+Arguments:
+
+ AccountSid - Sid of account
+
+ Privileges - Optional pointer to new Privilege Set. These flags
+ will overwrite the old value. if NULL is specified, a Privilege
+ Set containing 0 entries will be written.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAP_DB_ACCOUNT Account = NULL;
+ PPRIVILEGE_SET OutputPrivileges = Privileges;
+
+ //
+ // Verify that the Account List is valid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!LsapDbIsCacheValid( AccountObject)) {
+
+ goto UpdatePrivilegesAccountError;
+ }
+
+ //
+ // Lookup the Account
+ //
+
+ Status = LsapDbLookupAccount( AccountSid, &Account );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UpdatePrivilegesAccountError;
+ }
+
+ //
+ // If NULL was specified for the Privileges, construct a Privilege Set
+ // having 0 entries.
+ //
+
+ if (OutputPrivileges == NULL) {
+
+ Status = STATUS_NO_MEMORY;
+
+ OutputPrivileges = MIDL_user_allocate( sizeof(PRIVILEGE_SET) );
+
+ if (OutputPrivileges == NULL) {
+
+ goto UpdatePrivilegesAccountError;
+ }
+
+ OutputPrivileges->PrivilegeCount = 0;
+ OutputPrivileges->Control = 0;
+ }
+
+ //
+ // If there is an existing Privilege Set in the cache, free it.
+ //
+
+ if (Account->Info.PrivilegeSet != NULL) {
+
+ MIDL_user_free( Account->Info.PrivilegeSet );
+ Account->Info.PrivilegeSet = NULL;
+ }
+
+ //
+ // Update the Privileges
+ //
+
+ Account->Info.PrivilegeSet = OutputPrivileges;
+
+UpdatePrivilegesAccountFinish:
+
+ return(Status);
+
+UpdatePrivilegesAccountError:
+
+ if (Account != NULL) {
+
+ Account->Info.PrivilegeSet = NULL;
+ }
+
+ goto UpdatePrivilegesAccountFinish;
+}
+
+
+NTSTATUS
+LsapDbCreateAccountList(
+ OUT PLSAP_DB_ACCOUNT_LIST AccountList
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates an empty Account List
+
+Arguments
+
+ AccountList - Pointer to Account List structure that will be initialized.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ AccountList->AccountCount = 0;
+
+ InitializeListHead( &AccountList->Links );
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbBuildAccountCache(
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs a cache for the Account objects. The cache
+ is a counted doubly linked list of blocks, one for each Account Object
+ found in the LSA Policy Database.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ SID_AND_ATTRIBUTES AccountSidAndAttributes;
+ ULONG EnumerationIndex, EnumerationContext;
+ LSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer;
+ PLSAPR_SID AccountSid = NULL;
+ PLSAP_DB_ACCOUNT Account = NULL;
+
+ //
+ // Ensure caching of Account objects is turned off.
+ //
+
+ LsapDbMakeCacheBuilding( AccountObject );
+
+ //
+ // Initialize the Account List header with a skeleton entry for the
+ // System Account.
+ //
+
+ Status = LsapDbCreateAccountList(&LsapDbAccountList);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto BuildAccountCacheError;
+ }
+
+ LsapDbMakeCacheInvalid( AccountObject );
+
+ //
+ // Enumerate each of the LSA Account objects
+ //
+
+ Status = STATUS_MORE_ENTRIES;
+ EnumerationContext = 0;
+
+ while (Status == STATUS_MORE_ENTRIES) {
+
+ //
+ // Enumerate the next bunch of accounts.
+ //
+
+ Status = LsarEnumerateAccounts(
+ LsapPolicyHandle,
+ &EnumerationContext,
+ &EnumerationBuffer,
+ LSAP_DB_BUILD_ACCOUNT_LIST_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // We might just have got the warning that there are no more
+ // accounts. Reset to STATUS_SUCCESS and break out.
+ //
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+
+ Status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ //
+ // We've got some more accounts. Add them to the Account List
+ //
+
+ for( EnumerationIndex = 0;
+ EnumerationIndex < EnumerationBuffer.EntriesRead;
+ EnumerationIndex++ ) {
+
+ AccountSid = EnumerationBuffer.Information[ EnumerationIndex ].Sid;
+
+ Status = LsapDbCreateAccount( AccountSid, &Account );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AccountSidAndAttributes.Sid = (PSID) AccountSid;
+ AccountSidAndAttributes.Attributes = 0;
+
+ Status = LsapDbSlowQueryAllInformationAccounts(
+ LsapPolicyHandle,
+ 1,
+ &AccountSidAndAttributes,
+ &Account->Info
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ Status = STATUS_MORE_ENTRIES;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto BuildAccountCacheError;
+ }
+
+ //
+ // Turn on caching for Account objects.
+ //
+
+ LsapDbMakeCacheValid( AccountObject );
+
+ Status = STATUS_SUCCESS;
+
+BuildAccountCacheFinish:
+
+ return(Status);
+
+BuildAccountCacheError:
+
+ LsapDbMakeCacheInvalid(AccountObject);
+ LsapDbMakeCacheUnsupported(AccountObject);
+ goto BuildAccountCacheFinish;
+}
+
diff --git a/private/lsa/server/dbadmin.c b/private/lsa/server/dbadmin.c
new file mode 100644
index 000000000..d80c46be1
--- /dev/null
+++ b/private/lsa/server/dbadmin.c
@@ -0,0 +1,947 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbadmin.c
+
+Abstract:
+
+ Local Security Authority - Database Administration
+
+ This file contains routines that perform general Lsa Database
+ administration functions
+
+Author:
+
+ Scott Birrell (ScottBi) August 27, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#include "lsasrvp.h"
+#include "dbp.h"
+#include "adtp.h"
+
+
+NTSTATUS
+LsapDbSetStates(
+ IN ULONG DesiredStatesSet
+ )
+
+/*++
+
+Routine Description:
+
+ This routine turns on special states in the Lsa Database. These
+ states can be turned off using LsapDbResetStates.
+
+Arguments:
+
+ DesiredStatesSet - Specifies the states to be set.
+
+ LSAP_DB_ACQUIRE_LOCK - Acquire the Lsa Database lock.
+
+ LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK - Acquire the Lsa Audit Log
+ Queue Lock.
+
+ LSAP_DB_ENABLE_NON_TRUSTED_ACCESS - Enabled for general access and
+ update by non-trusted clients.
+
+ LSAP_DB_START_TRANSACTION - Start an Lsa Database transaction. There
+ must not already be one in progress.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_STATE - The Database is not in the correct state
+ to allow this state change.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ ULONG StatesSetHere = 0;
+
+ //
+ // If requested, lock the Audit Log Queue
+ //
+
+ if (DesiredStatesSet & LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK) {
+
+ Status = LsapAdtAcquireLogQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetStatesError;
+ }
+
+ StatesSetHere |= LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK;
+ }
+
+ //
+ // If requested, lock the Lsa database
+ //
+
+ if (DesiredStatesSet & LSAP_DB_ACQUIRE_LOCK) {
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetStatesError;
+ }
+
+ StatesSetHere |= LSAP_DB_ACQUIRE_LOCK;
+ }
+
+
+ //
+ // If requested, enable the database for access by non-trusted clients.
+ //
+
+ if (DesiredStatesSet & LSAP_DB_ENABLE_NON_TRUSTED_ACCESS) {
+
+ Status = LsapDbEnableNonTrustedAccess();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetStatesError;
+ }
+
+ StatesSetHere |= LSAP_DB_ENABLE_NON_TRUSTED_ACCESS;
+ }
+
+
+ //
+ // If requested, open a database update transaction.
+ //
+
+ if (DesiredStatesSet & LSAP_DB_START_TRANSACTION) {
+
+ Status = LsapDbOpenTransaction();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetStatesError;
+ }
+
+ StatesSetHere |= LSAP_DB_START_TRANSACTION;
+ }
+
+
+SetStatesFinish:
+
+ return( Status );
+
+SetStatesError:
+
+ //
+ // If we started a transaction, abort it.
+ //
+
+ if (StatesSetHere & LSAP_DB_START_TRANSACTION) {
+
+ SecondaryStatus = LsapDbAbortTransaction();
+ }
+
+ //
+ // If we disabled non-trusted access, re-enable it.
+ //
+
+ if (StatesSetHere & LSAP_DB_DISABLE_NON_TRUSTED_ACCESS) {
+
+ SecondaryStatus = LsapDbEnableNonTrustedAccess();
+ }
+
+ //
+ // If we locked the database, unlock it.
+ //
+
+ if (StatesSetHere & LSAP_DB_ACQUIRE_LOCK) {
+
+ LsapDbReleaseLock();
+ }
+
+ //
+ // If we locked the Audit Log Queue, unlock it.
+ //
+
+ if (StatesSetHere & LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK) {
+
+ LsapAdtReleaseLogQueueLock();
+ }
+
+ goto SetStatesFinish;
+}
+
+
+NTSTATUS
+LsapDbResetStates(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG Options,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
+ IN NTSTATUS PreliminaryStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function resets the Lsa Database states specified. It is used
+ to reset states set by LsapDbSetStates.
+
+Arguments:
+
+ ObjectHandle - Handle to an LSA object. This is expected to have
+ already been validated.
+
+ Options - Specifies optional actions, including states to be reset
+
+ LSAP_DB_RELEASE_LOCK - Lsa Database lock to be released
+
+ LSAP_DB_RELEASE_LOG_QUEUE_LOCK - Lsa Audit Log Queue Lock to
+ be released.
+
+ LSAP_DB_FINISH_TRANSACTION - Lsa database transaction open.
+
+ LSAP_DB_DISABLE_NON_TRUSTED_ACCESS - Disable the Lsa Database for
+ access by non-trusted clients.
+
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
+ Replicators.
+
+ PreliminaryStatus - Indicates the preliminary result code of the
+ calling routine. Allows reset action to vary depending on the
+ result code, for example, apply or abort transaction.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code. This is the final status to be used
+ by the caller and is equal to the Preliminary status except in the
+ case where that is as success status and this routine fails.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG StatesResetSuccessfully = 0;
+ ULONG StatesResetAttempted = 0;
+
+ //
+ // If requested, disable the database for access by non-trusted clients.
+ //
+
+ if (Options & LSAP_DB_DISABLE_NON_TRUSTED_ACCESS) {
+
+ StatesResetAttempted |= LSAP_DB_DISABLE_NON_TRUSTED_ACCESS;
+ Status = LsapDbDisableNonTrustedAccess();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ResetStatesError;
+ }
+
+ StatesResetSuccessfully |= LSAP_DB_DISABLE_NON_TRUSTED_ACCESS;
+ }
+
+ //
+ // If requested, finish a database update transaction.
+ //
+
+ if (Options & LSAP_DB_FINISH_TRANSACTION) {
+
+ StatesResetAttempted |= LSAP_DB_FINISH_TRANSACTION;
+
+ if (NT_SUCCESS(PreliminaryStatus)) {
+
+ Status = LsapDbApplyTransaction(
+ ObjectHandle,
+ Options,
+ SecurityDbDeltaType
+ );
+
+ } else {
+
+ Status = LsapDbAbortTransaction();
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ResetStatesError;
+ }
+
+ StatesResetSuccessfully |= LSAP_DB_FINISH_TRANSACTION;
+ }
+
+ //
+ // If unlocking requested, unlock the Lsa Database.
+ //
+
+ if (Options & LSAP_DB_RELEASE_LOCK) {
+
+ StatesResetAttempted |= LSAP_DB_RELEASE_LOCK;
+ LsapDbReleaseLock();
+ StatesResetSuccessfully |= LSAP_DB_RELEASE_LOCK;
+ }
+
+ //
+ // If unlocking if the Audit Log Queue requested, unlock the queue.
+ //
+
+ if (Options & LSAP_DB_RELEASE_LOG_QUEUE_LOCK) {
+
+ StatesResetAttempted |= LSAP_DB_RELEASE_LOG_QUEUE_LOCK;
+ LsapAdtReleaseLogQueueLock();
+ StatesResetSuccessfully |= LSAP_DB_RELEASE_LOG_QUEUE_LOCK;
+ }
+
+ //
+ // The requested reset operations were performed successfully.
+ // Propagate the preliminary status back to the caller.
+ //
+
+ Status = PreliminaryStatus;
+
+ResetStatesFinish:
+
+ return( Status );
+
+ResetStatesError:
+
+ //
+ // One or more of the requested reset operations could not be performed.
+ // Attempt to restore the database to a usable state.
+ //
+
+ LsapDbResetStatesError(
+ ObjectHandle,
+ PreliminaryStatus,
+ Options,
+ SecurityDbDeltaType,
+ StatesResetAttempted
+ );
+
+ goto ResetStatesFinish;
+}
+
+
+VOID
+LsapDbResetStatesError(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN NTSTATUS PreliminaryStatus,
+ IN ULONG Options,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
+ IN ULONG StatesResetAttempted
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to restore the Lsa Database state to a usable
+ form after a call to LsapDbResetStates() has failed. It will attempt
+ resets that were not attempted by that function because an error
+ occurred.
+
+Arguments:
+
+ ObjectHandle - Handle to an LSA object. This is expected to have
+ already been validated.
+
+ PreliminaryStatus - The preliminary Result Code that the caller of
+ LsapDbResetStates had. This is normally propagated back by that
+ caller.
+
+ Options - Specifies optional actions, including states to be reset
+
+ LSAP_DB_RELEASE_LOCK - Lsa Database lock to be released
+
+ LSAP_DB_RELEASE_LOG_QUEUE_LOCK - Lsa Audit Log Queue Lock to
+ be released.
+
+ LSAP_DB_FINISH_TRANSACTION - Lsa database transaction open.
+
+ LSAP_DB_DISABLE_NON_TRUSTED_ACCESS - Disable the Lsa Database for
+ access by non-trusted clients.
+
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
+ Replicators.
+
+ LSAP_DB_REBUILD_CACHE - Rebuild the cache for the object's type.
+ Note the the cache is normally rebuilt only if the
+ Preliminary Status was success and the Final Status was
+ a failure.
+
+ StatesResetAttempted - Specifies the state resets that were actually
+ attempted.
+--*/
+
+{
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ NTSTATUS IgnoreStatus;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+
+ //
+ // If disabling the database for access by non-trusted clients was
+ // requested but not attempted, do it now. SecondaryStatus is
+ // intentionally NOT checked afterwards.
+ //
+
+ if (Options & LSAP_DB_DISABLE_NON_TRUSTED_ACCESS) {
+
+ SecondaryStatus = LsapDbDisableNonTrustedAccess();
+ }
+
+ //
+ // If finishing of a database update transaction was requested but
+ // not attempted, do it now. SecondaryStatus is intentionally NOT
+ // checked afterwards.
+ //
+
+ if ((Options & LSAP_DB_FINISH_TRANSACTION) &&
+ !(StatesResetAttempted & LSAP_DB_FINISH_TRANSACTION)) {
+
+ if (NT_SUCCESS(PreliminaryStatus)) {
+
+ SecondaryStatus = LsapDbApplyTransaction(
+ ObjectHandle,
+ Options,
+ SecurityDbDeltaType
+ );
+
+ } else {
+
+ SecondaryStatus = LsapDbAbortTransaction();
+ }
+ }
+
+ //
+ // If the PreliminaryStatus was successful, attempt to rebuild
+ // the cache for this object type.
+ //
+
+ if (NT_SUCCESS(PreliminaryStatus) || (Options & LSAP_DB_REBUILD_CACHE)) {
+
+ ObjectTypeId = ((LSAP_DB_HANDLE) ObjectHandle)->ObjectTypeId;
+
+ IgnoreStatus = LsapDbRebuildCache( ObjectTypeId );
+ }
+
+ //
+ // If an unlock of the database was requested but not attempted,
+ // do this now.
+ //
+
+ if ((Options & LSAP_DB_RELEASE_LOCK) &&
+ !(StatesResetAttempted & LSAP_DB_RELEASE_LOCK)) {
+
+ LsapDbReleaseLock();
+ }
+
+
+ //
+ // If an unlock of the Audlt Log Queue was requested but not attempted,
+ // do this now.
+ //
+
+ if ((Options & LSAP_DB_RELEASE_LOG_QUEUE_LOCK) &&
+ !(StatesResetAttempted & LSAP_DB_RELEASE_LOG_QUEUE_LOCK)) {
+
+ LsapAdtReleaseLogQueueLock();
+ }
+}
+
+
+NTSTATUS
+LsapDbAcquireLock(
+ )
+
+/*++
+
+Routine Description:
+
+ This function acquires the LSA Database Lock. This lock serializes
+ most Lsa Database operations whether read or update. It is
+ typically acquired near the beginning of an Lsa Database API server
+ worker routine and released near the end of the routine.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = RtlEnterCriticalSection(&LsapDbState.DbLock);
+
+ return Status;
+}
+
+
+VOID
+LsapDbReleaseLock(
+ )
+
+/*++
+
+Routine Description:
+
+ This function releases the LSA Database Lock. This lock serializes
+ most Lsa Database operations whether read or update. It is
+ typically acquired near the beginning of an Lsa Database API server
+ worker routine and released near the end of the routine.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None. Any error occurring within this routine is an internal error.
+
+--*/
+
+{
+ RtlLeaveCriticalSection(&LsapDbState.DbLock);
+}
+
+
+NTSTATUS
+LsapDbEnableNonTrustedAccess(
+ )
+
+/*++
+
+Routine Description:
+
+ This function changes the LSA Database State to allow access by non-
+ Trusted clients.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_INTERNAL_DB_ERROR - An internal error has been detected in
+ the LSA Database.
+
+ STATUS_INVALID_SERVER_STATE - The Lsa Database is in the wrong
+ state for this operation. Specifically, non-trusted access
+ is already enables.
+
+ Errors from the Registry package.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ // TBS
+
+ return( Status );
+}
+
+
+NTSTATUS
+LsapDbDisableNonTrustedAccess(
+ )
+
+/*++
+
+Routine Description:
+
+ This function changes the LSA Database State to disallow access by non-
+ Trusted clients.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_INTERNAL_DB_ERROR - An internal error has been detected in
+ the LSA Database.
+
+ STATUS_INVALID_SERVER_STATE - The Lsa Database is in the wrong
+ state for this operation. Specifically, non-trusted access
+ is already disabled.
+
+ Errors from the Registry package.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ // TBS
+
+ return( Status );
+}
+
+
+NTSTATUS
+LsapDbOpenTransaction(
+ )
+
+/*++
+
+Routine Description:
+
+ This function starts a transaction within the LSA Database.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Result codes are those returned from the Registry Transaction
+ Package.
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Start the Regisrtry Transaction
+ //
+
+ Status = RtlStartRXact(LsapDbState.RXactContext);
+
+ if (NT_SUCCESS(Status)) {
+
+ LsapDbState.TransactionOpen = TRUE;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbApplyTransaction(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG Options,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType
+ )
+
+/*++
+
+Routine Description:
+
+ This function applies a transaction within the LSA Database.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ ObjectHandle - Handle to an LSA object. This is expected to have
+ already been validated.
+
+ Options - Specifies optional actions to be taken. The following
+ options are recognized, other options relevant to calling routines
+ are ignored.
+
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
+ Replicator.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Result codes are those returned from the Registry Transaction
+ Package.
+--*/
+
+{
+ NTSTATUS Status;
+ LARGE_INTEGER PromotionIncrement = LSA_PROMOTION_INCREMENT,
+ Increment = {1,0},
+ OriginalModifiedId;
+
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Except in the cases where we are installing the Policy Object,
+ // we are a Backup Domain Controller, or we are to omit replicator
+ // notification (e.g. for creation of a local secret), increment
+ // the Policy Database Modification Count and notify any replicator
+ // of the change.
+ //
+
+ if ((LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole != PolicyServerRoleBackup) &&
+ (!(Options & LSAP_DB_OMIT_REPLICATOR_NOTIFICATION)) &&
+ (LsapDbHandle != NULL)) {
+
+ OriginalModifiedId = LsapDbState.PolicyModificationInfo.ModifiedId;
+
+ //
+ // Increment Modification Count.
+ //
+
+ LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart =
+ LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart +
+ Increment.QuadPart;
+
+ if (Options & LSAP_DB_PROMOTION_INCREMENT) {
+ LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart =
+ LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart +
+ PromotionIncrement.QuadPart;
+ }
+
+ Status = LsapDbWriteAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[ PolMod ],
+ &LsapDbState.PolicyModificationInfo,
+ (ULONG) sizeof (POLICY_MODIFICATION_INFO)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ApplyTransactionError;
+ }
+
+ //
+ // Invalidate the cache for the Policy Modification Information
+ //
+
+ LsapDbMakeInvalidInformationPolicy( PolicyModificationInformation );
+
+ //
+ // Apply the Registry Transaction.
+ //
+
+ Status = RtlApplyRXact(LsapDbState.RXactContext);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ApplyTransactionError;
+ }
+
+ //
+ // Notify the Replicator
+ //
+
+ Status = LsapDbNotifyChangeObject( ObjectHandle, SecurityDbDeltaType );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ApplyTransactionError;
+ }
+
+ } else {
+
+ //
+ // Apply the Registry Transaction.
+ //
+
+ Status = RtlApplyRXact(LsapDbState.RXactContext);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ApplyTransactionError;
+ }
+ }
+
+ApplyTransactionFinish:
+
+ return( Status );
+
+ApplyTransactionError:
+
+ //
+ // Transaction failed. Adjust in-memory copy of the Modification
+ // Count, noting that backing store copy is unaltered.
+ //
+
+ LsapDbState.PolicyModificationInfo.ModifiedId =
+ OriginalModifiedId;
+
+
+ //
+ // abort the transaction
+ //
+
+ (VOID) RtlAbortRXact( LsapDbState.RXactContext );
+
+ goto ApplyTransactionFinish;
+}
+
+
+
+NTSTATUS
+LsapDbAbortTransaction(
+ )
+
+/*++
+
+Routine Description:
+
+ This function aborts a transaction within the LSA Database.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Result codes are those returned from the Registry Transaction
+ Package.
+--*/
+
+{
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Abort the Registry Transaction
+ //
+
+ LsapDbState.TransactionOpen = FALSE;
+ return RtlAbortRXact(LsapDbState.RXactContext);
+}
+
+
+BOOLEAN
+LsapDbOpenedTransaction(
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if there is an open LSA Database Transaction.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if there is an open transaction, else FALSE
+
+--*/
+
+{
+ return LsapDbState.TransactionOpen;
+}
+
+
+BOOLEAN
+LsapDbIsServerInitialized(
+ )
+
+/*++
+
+Routine Description:
+
+ This function indicates whether the Lsa Database Server is initialized.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if the LSA Database Server is initialized, else FALSE.
+
+--*/
+
+{
+ if (LsapDbState.DbServerInitialized) {
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+ }
+}
+
+
+VOID
+LsapDbEnableReplicatorNotification(
+ )
+
+/*++
+
+Routine Description:
+
+ This function turns on Replicator Notification.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ LsapDbState.ReplicatorNotificationEnabled = TRUE;
+}
+
+VOID
+LsapDbDisableReplicatorNotification(
+ )
+
+/*++
+
+Routine Description:
+
+ This function turns off Replicator Notification.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ LsapDbState.ReplicatorNotificationEnabled = FALSE;
+}
diff --git a/private/lsa/server/dbattr.c b/private/lsa/server/dbattr.c
new file mode 100644
index 000000000..1c208a05b
--- /dev/null
+++ b/private/lsa/server/dbattr.c
@@ -0,0 +1,839 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbattr.c
+
+Abstract:
+
+ LSA Database Handle Manager - Object Attribute Routines
+
+ These routines manipulate or construct LSA Database Object Attributes
+ or their content.
+
+Author:
+
+ Scott Birrell (ScottBi) January 21, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+NTSTATUS
+LsapDbMakeUnicodeAttribute(
+ IN OPTIONAL PUNICODE_STRING UnicodeValue,
+ IN PUNICODE_STRING AttributeName,
+ OUT PLSAP_DB_ATTRIBUTE Attribute
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs Attribute Information for an attribute value
+ that is in Unicode String form. The Unicode String is converted to
+ Self-Relative form after validation and the given Attribute
+ structure is filled in.
+
+ If a NULL UnicodeValue, or string of length 0 is specified, NULL is
+ propagated as the attribute value.
+
+ WARNING! - This routine allocates memory for the Self-Relative Unicode
+ string produced. This memory must be freed after use by calling
+ MIDL_user_free()
+
+Arguments:
+
+ UnicodeValue - Pointer to Unicode String containing the Attribute's
+ Value. NULL may be specified, in which case, NULL will be stored
+ in the output Attribute.
+
+ AttributeName - Pointer to the Unicode name of the attribute.
+
+ Attribute - Pointer to structure that will receive the
+ attributes's information. This consists of the attribute's name,
+ value and value length.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources such
+ as memory to complete the call.
+
+ STATUS_INVALID_PARAMETER - The specified AttributeValue is not a
+ pointer to a Unicode String.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PUNICODE_STRING OutputAttributeValue = NULL;
+ ULONG OutputAttributeValueLength = 0;
+
+ //
+ // Mark attribute initially as not having had memory allocated by
+ // setting MemoryAllocated to FALSE. If routine succeeds and we allocate
+ // memory via MIDL_user_allocate() change MemoryAllocated field to TRUE.
+ //
+
+ Attribute->MemoryAllocated = FALSE;
+
+ if (ARGUMENT_PRESENT(UnicodeValue) && UnicodeValue->Length != 0) {
+
+ //
+ // Calculate the size of memory required for a Self-Relative
+ // Unicode String and allocate the memory.
+ //
+
+ OutputAttributeValueLength =
+ sizeof(UNICODE_STRING) + (ULONG) UnicodeValue->MaximumLength;
+ OutputAttributeValue = MIDL_user_allocate(OutputAttributeValueLength);
+ Attribute->MemoryAllocated = TRUE;
+
+ if (OutputAttributeValue == NULL) {
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ //
+ // Setup self-relative Unicode String (but with absolute buffer pointer
+ // referencing buffer following UNICODE_STRING header)
+ // Copy source Unicode Value to Self-relative Unicode String. Set buffer pointer
+ // to NULL as it will not be used here.
+ //
+
+ OutputAttributeValue->Length = UnicodeValue->Length;
+ OutputAttributeValue->MaximumLength = UnicodeValue->MaximumLength;
+ OutputAttributeValue->Buffer = (PWSTR)(OutputAttributeValue + 1);
+
+ //
+ // Copy the Unicode string Buffer
+ //
+
+ RtlCopyUnicodeString( OutputAttributeValue, UnicodeValue );
+
+ //
+ // Set the buffer pointer to the relative offset of the data.
+ //
+
+ OutputAttributeValue->Buffer = (PVOID) sizeof(UNICODE_STRING);
+
+ }
+
+ Attribute->AttributeName = AttributeName;
+ Attribute->AttributeValue = OutputAttributeValue;
+ Attribute->AttributeValueLength = OutputAttributeValueLength;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbMakeMultiUnicodeAttribute(
+ OUT PLSAP_DB_ATTRIBUTE Attribute,
+ IN PUNICODE_STRING AttributeName,
+ IN PUNICODE_STRING UnicodeStrings,
+ IN ULONG Entries
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs an attribute value containing one or more
+ Unicode Strings in Self-Relative format. Memory for the attribute's
+ value will be allocated via MIDL_user_allocate and must be freed
+ when no longer required via MIDL_user_free.
+
+ If a NULL UnicodeValue, or string of length 0 is specified, NULL is
+ propagated as the attribute value.
+
+ WARNING! The caller is expected to provide valid parameters. No
+ checking will be done.
+
+Arguments:
+
+ Attribute - Pointer to attribute structure that will be initialized
+ to reference the newly constructed value.
+
+ AttributeName - Pointer to Unicode string specifying the name of
+ the attribute to be constructed.
+
+ UnicodeStrings - Pointer to an array of Unicode String structures.
+
+ Entries - Number of Unicode Strings specified in the array. Zero
+ is specifiable.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG MultiUnicodeStringLength;
+ PLSAP_DB_MULTI_UNICODE_STRING MultiUnicodeString;
+ PUNICODE_STRING NextInputUnicodeString;
+ PUNICODE_STRING NextOutputUnicodeString;
+ PUNICODE_STRING LastInputUnicodeString;
+ PUCHAR NextOutputUnicodeBuffer;
+ BOOLEAN MemoryAllocated;
+
+ //
+ // If the number of strings is Zero, initialize the attribute to
+ // NULL.
+ //
+
+ if (Entries != 0) {
+
+ LastInputUnicodeString = &UnicodeStrings[Entries - 1];
+
+ //
+ // Calculate the amount of memory required for the
+ // Multi-Unicode string. First get the size of the header.
+ //
+
+ MultiUnicodeStringLength =
+ sizeof (LSAP_DB_MULTI_UNICODE_STRING) +
+ ((Entries - 1) * sizeof (UNICODE_STRING));
+
+ //
+ // Now add in the length of each Unicode Buffer.
+ //
+
+ for( NextInputUnicodeString = UnicodeStrings;
+ NextInputUnicodeString <= LastInputUnicodeString;
+ NextInputUnicodeString++
+ ) {
+
+ MultiUnicodeStringLength += NextInputUnicodeString->Length;
+ }
+
+ //
+ // Now allocate the memory.
+ //
+
+ MultiUnicodeString = MIDL_user_allocate( MultiUnicodeStringLength );
+
+ if (MultiUnicodeString == NULL) {
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ //
+ // Copy in each Unicode String, making it Self-Relative as we go.
+ // The Unicode String buffers are placed right after the end of
+ // the array of Unicode String structures.
+ //
+
+ NextOutputUnicodeBuffer =
+ (PUCHAR)(MultiUnicodeString->UnicodeStrings + Entries);
+
+ for ( NextInputUnicodeString = UnicodeStrings,
+ NextOutputUnicodeString = MultiUnicodeString->UnicodeStrings;
+ NextInputUnicodeString <= LastInputUnicodeString;
+ NextInputUnicodeString++, NextOutputUnicodeString++
+ ) {
+
+ //
+ // First copy the Unicode String Structure.
+ //
+
+ *NextOutputUnicodeString = *NextInputUnicodeString;
+
+ //
+ // Now replace the absolute pointer to the Unicode Buffer
+ // with a self-relative offset. Note that this offset
+ // is relative to the start of the Unicode String structure,
+ // NOT relative to the start of the MultiUnicodeString.
+ //
+
+ NextOutputUnicodeString->Buffer =
+ (PWSTR) (NextOutputUnicodeBuffer - (PUCHAR) NextOutputUnicodeString);
+
+ //
+ // Copy in the Unicode Buffer.
+ //
+
+ RtlMoveMemory(
+ NextOutputUnicodeBuffer,
+ NextInputUnicodeString->Buffer,
+ NextInputUnicodeString->Length
+ );
+
+ //
+ // Update destination Unicode Buffer pointer
+ //
+
+ NextOutputUnicodeBuffer += NextInputUnicodeString->Length;
+ }
+
+ MultiUnicodeString->Entries = Entries;
+ MemoryAllocated = TRUE;
+
+ } else {
+
+ MultiUnicodeString = NULL;
+ MultiUnicodeStringLength = 0;
+ MemoryAllocated = FALSE;
+ }
+
+ //
+ // Initialize the output attribute structure.
+ //
+
+ LsapDbInitializeAttribute(
+ Attribute,
+ AttributeName,
+ MultiUnicodeString,
+ MultiUnicodeStringLength,
+ MemoryAllocated
+ );
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbCopyUnicodeAttribute(
+ OUT PUNICODE_STRING OutputString,
+ IN PLSAP_DB_ATTRIBUTE Attribute,
+ IN BOOLEAN SelfRelative
+ )
+
+/*++
+
+Routine Description:
+
+This function makes a UNICODE_STRING structure reference the value of
+an attribute that has a Unicode String as its value. Memory for the
+attribute values's Unicode Buffer is allocated via MIDL_user_allocate.
+
+Arguments:
+
+ OutputString - Pointer to UNICODE_STRING structure that will be made
+ to reference the attribute value's Unicode Buffer.
+
+ Attribute - Pointer to attribute information block whose
+ AttributeValue field is a pointer to a Unicode String,
+ or NULL. If NULL or if the string has length 0, the output Unicode String is initialized
+ with a buffer pointer equal to NULL and a zero length.
+
+ SelfRelative - TRUE if the input Unicode String is expected to be
+ in Self-Relative form, else FALSE.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call was successful
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ UNICODE_STRING AbsInputUnicodeString;
+ PUNICODE_STRING ReturnedUnicodeString = NULL;
+
+ //
+ // Obtain pointer to input Unicode String contained in Attribute.
+ // Convert it to absolute form if necessary.
+ //
+
+ PUNICODE_STRING InputUnicodeString =
+ (PUNICODE_STRING) Attribute->AttributeValue;
+
+ if ((InputUnicodeString != NULL) && (InputUnicodeString->Length != 0)) {
+
+ AbsInputUnicodeString.Length = InputUnicodeString->Length;
+ AbsInputUnicodeString.MaximumLength = InputUnicodeString->MaximumLength;
+ AbsInputUnicodeString.Buffer = InputUnicodeString->Buffer;
+
+ if (SelfRelative) {
+
+ AbsInputUnicodeString.Buffer =
+ (PWSTR)
+ (((PUCHAR)(InputUnicodeString)) +
+ (ULONG)(InputUnicodeString->Buffer));
+ InputUnicodeString = &AbsInputUnicodeString;
+ }
+
+ //
+ // Now allocate memory for the Unicode String Buffer.
+ //
+
+ OutputString->Buffer =
+ MIDL_user_allocate(InputUnicodeString->MaximumLength);
+
+ if (OutputString->Buffer == NULL) {
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ //
+ // Initialize UNICODE_STRING header
+ //
+
+ OutputString->Length = InputUnicodeString->Length;
+ OutputString->MaximumLength = InputUnicodeString->MaximumLength;
+
+ //
+ // Copy the input Unicode String
+ //
+
+ RtlCopyUnicodeString( OutputString, InputUnicodeString );
+
+ } else {
+
+ //
+ // The attribute contains a NULL Unicode String or one of length
+ // 0. Set the output Unicode String to NULL.
+ //
+
+ OutputString->Length = OutputString->MaximumLength = 0;
+ OutputString->Buffer = (PWSTR) NULL;
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbCopyMultiUnicodeAttribute(
+ IN PLSAP_DB_ATTRIBUTE Attribute,
+ OUT PULONG Entries,
+ OUT PUNICODE_STRING *OutputStrings
+ )
+
+/*++
+
+Routine Description:
+
+This function makes an array of UNICODE_STRING structures reference the
+values of an attribute that has a multi-Unicode String array as its value.
+Memory for the Unicode Buffers is allocated via individual
+MIDL_user_allocate calls. This memory must be freed when no longer required
+via MIDL_user_free.
+
+Arguments:
+
+ Attribute - Pointer to Multi Unicode Attribute to be copied. The
+ AttributeValue field may be NULL.
+
+ Entries - Pointer to location which will receive the number of
+ entries read.
+
+ OutputStrings - Receives a pointer to an array of UNICODE_STRING
+ structures that will be made to reference the attribute value's
+ Unicode Buffers.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call was successful
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_MULTI_UNICODE_STRING MultiUnicodeString;
+ ULONG UnicodeStringHeaderLength;
+ PUNICODE_STRING NextInputUnicodeString;
+ PUNICODE_STRING NextOutputUnicodeString;
+ PUNICODE_STRING LastInputUnicodeString;
+ PUNICODE_STRING LastOutputUnicodeString;
+ UNICODE_STRING AbsNextInputUnicodeString;
+ PUNICODE_STRING OutputUnicodeStrings = NULL;
+
+ MultiUnicodeString =
+ (PLSAP_DB_MULTI_UNICODE_STRING) Attribute->AttributeValue;
+
+ //
+ // If there are no entries to copy, just return zero count and exit.
+ //
+
+ if ((MultiUnicodeString == NULL) || MultiUnicodeString->Entries == 0) {
+
+ *Entries = 0;
+ return(Status);
+ }
+
+ //
+ // Allocate memory for the array of Unicode String structures.
+ //
+
+ UnicodeStringHeaderLength =
+ MultiUnicodeString->Entries * sizeof (UNICODE_STRING);
+ OutputUnicodeStrings = MIDL_user_allocate( UnicodeStringHeaderLength );
+
+ if (OutputUnicodeStrings == NULL) {
+
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Now copy the Unicode String structures over.
+ //
+
+ RtlMoveMemory(
+ OutputUnicodeStrings,
+ MultiUnicodeString->UnicodeStrings,
+ UnicodeStringHeaderLength
+ );
+
+ LastInputUnicodeString =
+ MultiUnicodeString->UnicodeStrings +
+ MultiUnicodeString->Entries - 1;
+
+ //
+ // Now copy the Unicode Buffers for each string.
+ //
+
+ for (NextInputUnicodeString = MultiUnicodeString->UnicodeStrings,
+ NextOutputUnicodeString = OutputUnicodeStrings;
+ NextInputUnicodeString <= LastInputUnicodeString;
+ NextInputUnicodeString++, NextOutputUnicodeString++
+ ) {
+
+ NextOutputUnicodeString->Buffer = NULL;
+
+ if (NextInputUnicodeString->Length != 0) {
+
+ NextOutputUnicodeString->Buffer =
+ MIDL_user_allocate( NextInputUnicodeString->MaximumLength );
+
+ if (NextOutputUnicodeString->Buffer == NULL) {
+
+ //
+ // Memory allocation failed. We need to free up all of
+ // the output Unicode buffers allocated to date.
+
+ LastOutputUnicodeString = NextOutputUnicodeString;
+
+ for (NextOutputUnicodeString = OutputUnicodeStrings;
+ NextOutputUnicodeString < LastOutputUnicodeString;
+ NextOutputUnicodeString++
+ ) {
+
+ if (NextOutputUnicodeString->Buffer != NULL) {
+
+ MIDL_user_free( NextOutputUnicodeString->Buffer );
+ }
+ }
+
+ //
+ // Free the buffer for the Output Unicode String headers
+ //
+
+ MIDL_user_free( OutputUnicodeStrings );
+ }
+
+ //
+ // Memory allocation for Unicode Buffer successful.
+ // Make absolute copy of input Unicode string's header and
+ // copy the Unicode String.
+ //
+
+ AbsNextInputUnicodeString = *NextInputUnicodeString;
+ AbsNextInputUnicodeString.Buffer =
+ (PWSTR)(((PUCHAR) NextInputUnicodeString) +
+ (ULONG)(NextInputUnicodeString->Buffer));
+
+ RtlCopyUnicodeString(
+ NextOutputUnicodeString,
+ &AbsNextInputUnicodeString
+ );
+ }
+ }
+
+ *Entries = MultiUnicodeString->Entries;
+ *OutputStrings = OutputUnicodeStrings;
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbMakeSidAttribute(
+ IN OPTIONAL PSID Sid,
+ IN PUNICODE_STRING AttributeName,
+ OUT PLSAP_DB_ATTRIBUTE Attribute
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs Attribute Information for an attribute value
+ that is in Sid form. The Sid is validated and the given
+ Attribute structure is filled in.
+
+Arguments:
+
+ Sid - Pointer to the Sid or NULL.
+
+ AttributeName - Pointer to the Unicode name of the attribute.
+
+ Attribute - Pointer to structure that will receive the
+ attributes's information. This consists of the attribute's name,
+ value and value length.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_PARAMETER - The specified AttributeValue is not a
+ pointer to a syntactically valid Sid, or NULL.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ Attribute->AttributeName = AttributeName;
+ Attribute->MemoryAllocated = FALSE;
+
+ if (ARGUMENT_PRESENT(Sid)) {
+
+ if (RtlValidSid(Sid)) {
+
+ Attribute->AttributeValue = Sid;
+ Attribute->AttributeValueLength = RtlLengthSid(Sid);
+ return(Status);
+ }
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // The supplied Sid is NULL or invalid.
+ //
+
+ Attribute->AttributeValue = NULL;
+ Attribute->AttributeValueLength = 0;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbReadAttribute(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN OUT PLSAP_DB_ATTRIBUTE Attribute
+ )
+
+/*++
+
+Routine Description:
+
+ This function reads an attribute of an object, allocating memory if
+ requested for the buffer containing the attribute's value.
+
+Arguments:
+
+ ObjectHandle - Handle to object obtained from LsapDbCreateObject or
+ LsapDbOpenObject
+
+ Attributes - Pointer to an array of Attribute Information blocks each
+ containing pointers to the attribute's Unicode Name, an optional
+ pointer to a buffer that will receive the value and an optional
+ length of the value expected in bytes.
+
+ If the AttributeValue field in this structure is specified as non-NULL,
+ the attribute's data will be returned in the specified buffer. In
+ this case, the AttributeValueLength field must specify a sufficiently
+ large buffer size in bytes. If the specified size is too small,
+ a warning is returned and the buffer size required is returned in
+ AttributeValueLength.
+
+ If the AttributeValue field in this structure is NULL, the routine
+ will allocate memory for the attribute value's buffer, via MIDL_user_allocate(). If
+ the AttributeValueLength field is non-zero, the number of bytes specified
+ will be allocated. If the size of buffer allocated is too small to
+ hold the attribute's value, a warning is returned. If the
+ AttributeValuelength field is 0, the routine will first query the size
+ of buffer required and then allocate its memory.
+
+ In all success cases and buffer overflow cases, the
+ AttributeValueLength is set upon exit to the size of data required.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ Attribute->MemoryAllocated = FALSE;
+
+ //
+ // If an explicit buffer pointer is given, verify that the length
+ // specified is non-zero and attempt to use that buffer.
+ //
+
+ if (Attribute->AttributeValue != NULL) {
+
+ if (Attribute->AttributeValueLength == 0) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ Status = LsapDbReadAttributeObject(
+ ObjectHandle,
+ Attribute->AttributeName,
+ Attribute->AttributeValue,
+ &Attribute->AttributeValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ReadAttributeError;
+ }
+
+ return(Status);
+ }
+
+ //
+ // No output buffer pointer has been given. If a zero buffer
+ // size is given, query size of memory required. Since the
+ // buffer length is 0, STATUS_SUCCESS should be returned rather
+ // than STATUS_BUFFER_OVERFLOW.
+ //
+
+ if (Attribute->AttributeValueLength == 0) {
+
+ Status = LsapDbReadAttributeObject(
+ ObjectHandle,
+ Attribute->AttributeName,
+ NULL,
+ &Attribute->AttributeValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ReadAttributeError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // If the attribute value size needed is 0, return NULL pointer
+ //
+
+ if (Attribute->AttributeValueLength == 0) {
+
+ Attribute->AttributeValue = NULL;
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // Allocate memory for the buffer.
+ //
+
+ Attribute->AttributeValue =
+ MIDL_user_allocate(Attribute->AttributeValueLength);
+
+ if (Attribute->AttributeValue == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ReadAttributeError;
+ }
+
+ Attribute->MemoryAllocated = TRUE;
+
+ //
+ // Now read the attribute into the buffer.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ ObjectHandle,
+ Attribute->AttributeName,
+ Attribute->AttributeValue,
+ &Attribute->AttributeValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ReadAttributeError;
+ }
+
+ReadAttributeFinish:
+
+ return(Status);
+
+ReadAttributeError:
+
+ //
+ // If memory was allocated for any values read, it must be freed.
+ //
+
+ if (Attribute->MemoryAllocated) {
+
+ MIDL_user_free( Attribute->AttributeValue );
+ }
+
+ goto ReadAttributeFinish;
+}
+
+
+NTSTATUS
+LsapDbFreeAttributes(
+ IN ULONG Count,
+ IN PLSAP_DB_ATTRIBUTE Attributes
+ )
+
+/*++
+
+Routine Description:
+
+ This function frees memory allocated for Attribute Values in an
+ array of attributes.
+
+Arguments:
+
+ Count - Count of attributes in the array
+
+ Attributes - Pointer to array of attributes. Only those attributes
+ in which MemoryAllocated is set to TRUE will have their
+ Attribute Value buffers freed. For these attributes, MemoryAllocated
+ will be set to false.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Index;
+
+ for (Index = 0; Index < Count; Index++) {
+
+ if (Attributes[Index].MemoryAllocated) {
+
+ MIDL_user_free(Attributes[Index].AttributeValue);
+ Attributes[Index].MemoryAllocated = FALSE;
+ Attributes[Index].AttributeValue = NULL;
+ Attributes[Index].AttributeValueLength = 0;
+ }
+ }
+
+ return(Status);
+}
+
+
diff --git a/private/lsa/server/dbdata.c b/private/lsa/server/dbdata.c
new file mode 100644
index 000000000..3cfe9fd5e
--- /dev/null
+++ b/private/lsa/server/dbdata.c
@@ -0,0 +1,436 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbdata.c
+
+Abstract:
+
+ Local Security Authority - Database Server Global Data
+
+Author:
+
+ Scott Birrell (ScottBi) July 25, 1991
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+OBJECT_ATTRIBUTES LsapDbObjectAttributes;
+STRING LsapDbNameString;
+LARGE_INTEGER LsapDbInitSize;
+LARGE_INTEGER LsapDbMaximumSizeOfSection;
+
+
+
+
+//
+// LSA Initialized Status
+//
+
+BOOLEAN LsapInitialized = FALSE;
+
+//
+// Setup Event Existed
+// This is necessary to distinguish a psuedo install done
+// during a developer's first boot after install (which does
+// an auto init) and the case where a real setup was run.
+//
+
+BOOLEAN LsapSetupWasRun = FALSE;
+
+
+//
+// Database initialization has been performed
+//
+
+BOOLEAN LsapDatabaseSetupPerformed = FALSE;
+
+//
+// Type of product we are running
+//
+
+NT_PRODUCT_TYPE LsapProductType;
+
+//
+// LSA Database State information
+//
+
+LSAP_DB_STATE LsapDbState;
+
+//
+// LsaDb object Handle used internally.
+// Also one for use throughout LSA.
+//
+
+LSAPR_HANDLE LsapDbHandle;
+LSAPR_HANDLE LsapPolicyHandle = NULL;
+
+//
+// LSA Database Encryption Key
+//
+
+PLSAP_CR_CIPHER_KEY LsapDbCipherKey;
+
+
+//
+// Queue of name/sid lookup activities.
+//
+
+LSAP_DB_LOOKUP_WORK_QUEUE LookupWorkQueue;
+
+
+//
+// LSA Database Object SubKey Unicode name string array
+//
+
+UNICODE_STRING LsapDbNames[DummyLastName];
+
+//
+// LSA Database Object Type Containing Directory Names
+//
+
+UNICODE_STRING LsapDbContDirs[DummyLastObject];
+
+//
+// Object Information Requirements. These arrays, indexed by object
+// type id indicated whether objects have Sids or Names.
+//
+// WARNING! - These arrays must be kept in sync with the LSAP_DB_OBJECT_TYPE_ID
+// enumerated type.
+//
+
+BOOLEAN LsapDbRequiresSidInfo[DummyLastObject] = {
+
+ FALSE, // NullObject
+ FALSE, // LsaDatabaseObject
+ FALSE, // BuiltInAccountObject
+ TRUE, // AccountObject
+ FALSE // SecretObject
+};
+
+BOOLEAN LsapDbRequiresNameInfo[DummyLastObject] = {
+
+ FALSE, // NullObject,
+ TRUE, // LsaDatabaseObject
+ TRUE, // BuiltInAccountObject
+ FALSE, // AccountObject
+ TRUE // SecretObject
+};
+
+//
+// Table of accesses required to query Policy Information. This table
+// is indexed by Policy Information Class
+//
+
+ACCESS_MASK LsapDbRequiredAccessQueryPolicy[PolicyAuditFullQueryInformation + 1] = {
+
+ 0, // Information classes start at 1
+ POLICY_VIEW_AUDIT_INFORMATION, // PolicyAuditLogInformation
+ POLICY_VIEW_AUDIT_INFORMATION, // PolicyAuditEventsInformation
+ POLICY_VIEW_LOCAL_INFORMATION, // PolicyPrimaryDomainInformation
+ POLICY_GET_PRIVATE_INFORMATION, // PolicyPdAccountInformation
+ POLICY_VIEW_LOCAL_INFORMATION, // PolicyAccountDomainInformation
+ POLICY_VIEW_LOCAL_INFORMATION, // PolicyLsaServerRoleInformation
+ POLICY_VIEW_LOCAL_INFORMATION, // PolicyReplicaSourceInformation
+ POLICY_VIEW_LOCAL_INFORMATION, // PolicyDefaultQuotaInformation
+ 0, // Not settable by non-trusted call
+ 0, // Not applicable
+ POLICY_VIEW_AUDIT_INFORMATION // PolicyAuditFullQueryInformation
+};
+
+//
+// Table of accesses required to set Policy Information. This table
+// is indexed by Policy Information Class
+//
+
+ACCESS_MASK LsapDbRequiredAccessSetPolicy[PolicyAuditFullQueryInformation + 1] = {
+
+ 0, // Information classes start at 1
+ POLICY_AUDIT_LOG_ADMIN, // PolicyAuditLogInformation
+ POLICY_SET_AUDIT_REQUIREMENTS, // PolicyAuditEventsInformation
+ POLICY_TRUST_ADMIN, // PolicyPrimaryDomainInformation
+ 0, // Not settable by non-trusted call
+ POLICY_TRUST_ADMIN, // PolicyAccountDomainInformation
+ POLICY_SERVER_ADMIN, // PolicyLsaServerRoleInformation
+ POLICY_SERVER_ADMIN, // PolicyReplicaSourceInformation
+ POLICY_SET_DEFAULT_QUOTA_LIMITS,// PolicyDefaultQuotaInformation
+ 0, // Not settable by non-trusted call
+ POLICY_AUDIT_LOG_ADMIN, // PolicyAuditFullSetInformation
+ 0 // Not applicable
+};
+
+
+//
+// Table of accesses required to query TrustedDomain Information. This table
+// is indexed by TrustedDomain Information Class
+//
+
+ACCESS_MASK LsapDbRequiredAccessQueryTrustedDomain[TrustedPosixOffsetInformation + 1] = {
+
+ 0, // Information classes start at 1
+ TRUSTED_QUERY_DOMAIN_NAME, // TrustedDomainNameInformation
+ TRUSTED_QUERY_CONTROLLERS, // TrustedControllersInformation
+ TRUSTED_QUERY_POSIX // TrustedPosixOffsetInformation
+};
+
+//
+// Table of accesses required to set TrustedDomain Information. This table
+// is indexed by TrustedDomain Information Class
+//
+
+ACCESS_MASK LsapDbRequiredAccessSetTrustedDomain[TrustedPosixOffsetInformation + 1] = {
+
+ 0, // Information classes start at 1
+ 0, // not settable (TrustedAccountNameInformation)
+ TRUSTED_SET_CONTROLLERS, // TrustedControllersInformation
+ TRUSTED_SET_POSIX // TrustedPosixOffsetInformation
+};
+
+
+//
+// Cached Policy Object. Only default Quota Limits is cached just now.
+//
+
+LSAP_DB_POLICY LsapDbPolicy;
+
+NTSTATUS
+LsaIGetPrivateData(
+ IN LSAPR_HANDLE PolicyHandle,
+ OUT PULONG DataLength,
+ OUT PVOID *Data
+ )
+
+/*++
+
+Routine Description:
+
+ This service is a required part of replication. Private LSA Database
+ information (i.e. not accessible via other LSA services even at the
+ trusted level) must be replicated the the beginning of each attempt
+ to replicate. This call is used to read Private LSA Database data from
+ the Primary Domain Controller (PDC) involved in a replication. The data
+ returned from this call must be provided to a corresponding
+ LsaISetPrivateData() call for each Backup Domain Controller involved
+ in the replication in order to update the private LSA Database information
+ on those controller(s).
+
+Arguments:
+
+ PolicyHandle - Trusted Handle to an Lsa Policy Object.
+
+ DataLength - Length in bytes of the Private Data.
+
+ Data - Pointer to the Private Data.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_ACCESS_DENIED - The specified PolicyHandle is not Trusted.
+
+ STATUS_INVALID_HANDLE - The specified PolicyHandle is not a
+ valid handle to a Policy Object.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN ObjectReferenced = FALSE;
+ PLSAP_DB_POLICY_PRIVATE_DATA PolicyPrivateData = NULL;
+ LSAP_DB_ATTRIBUTE Attribute;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the handle is a valid
+ // Trusted handle to the Policy object. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_TRUSTED
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GetPrivateDataError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Read the private LSA Database Data.
+ //
+
+ Attribute.AttributeName = &LsapDbNames[PolState];
+ Attribute.AttributeValue = NULL;
+ Attribute.AttributeValueLength = sizeof (LSAP_DB_POLICY_PRIVATE_DATA);
+ Attribute.MemoryAllocated = FALSE;
+
+ Status = LsapDbReadAttribute( PolicyHandle, &Attribute );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GetPrivateDataError;
+ }
+
+GetPrivateDataFinish:
+
+ *Data = Attribute.AttributeValue;
+ *DataLength = Attribute.AttributeValueLength;
+
+ //
+ // If necessary, dereference the Policy Object, release the LSA Database lock and
+ // return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+GetPrivateDataError:
+
+ Attribute.AttributeValueLength = 0;
+
+ //
+ // If necessary, free the returned buffer.
+ //
+
+ if ( Attribute.AttributeValue != NULL ) {
+
+ MIDL_user_free( Attribute.AttributeValue );
+ Attribute.AttributeValue = NULL;
+ }
+
+ goto GetPrivateDataFinish;
+}
+
+
+
+NTSTATUS
+LsaISetPrivateData(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG DataLength,
+ IN PVOID Data
+ )
+
+/*++
+
+Routine Description:
+
+ This service is a required part of replication. Private LSA Database
+ information (i.e. not accessible via other LSA services even at the
+ trusted level) must be replicated the the beginning of each attempt
+ to replicate. Information specified on this call must have been
+ obtained from a corresponding LsaIGetPrivateData() call that accessed
+ the Primary Domain Controller (PDC) involved in a replication.
+
+Arguments:
+
+ PolicyHandle - Trusted Handle to the Lsa Policy Object of a
+ Backup Domain Controller (BDC) involved in a repilcation.
+
+ ModifiedCount - Specifies a value to be set for the current count of
+ modifications made to the LSA Database.
+
+ CreationTime - Specifies a value to be set for the date and time at
+ which the LSA Database was created.
+
+ DataLength - Length in bytes of the Private Data.
+
+ Data - Pointer to the Private Data.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_ACCESS_DENIED - The specified PolicyHandle is not Trusted.
+
+ STATUS_INVALID_HANDLE - The specified PolicyHandle is not a
+ valid handle to a Policy Object.
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN ObjectReferenced = FALSE;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the handle is a valid
+ // Trusted handle to the Policy object. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_TRUSTED
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetPrivateDataError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ Status = LsapDbWriteAttributeObject(
+ PolicyHandle,
+ &LsapDbNames[PolState],
+ Data,
+ DataLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetPrivateDataError;
+ }
+
+SetPrivateDataFinish:
+
+ //
+ // If necessary, dereference the Policy Object, release the LSA Database lock and
+ // return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+SetPrivateDataError:
+
+ goto SetPrivateDataFinish;
+}
diff --git a/private/lsa/server/dbdomain.c b/private/lsa/server/dbdomain.c
new file mode 100644
index 000000000..022d98cf1
--- /dev/null
+++ b/private/lsa/server/dbdomain.c
@@ -0,0 +1,4325 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbdomain.c
+
+Abstract:
+
+ LSA Database - Trusted Domain Object Private API Workers
+
+ NOTE: This module should remain as portable code that is independent
+ of the implementation of the LSA Database. As such, it is
+ permitted to use only the exported LSA Database interfaces
+ contained in db.h and NOT the private implementation
+ dependent functions in dbp.h.
+
+Author:
+
+ Scott Birrell (ScottBi) January 13, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+
+LSAP_DB_TRUSTED_DOMAIN_LIST LsapDbTrustedDomainList;
+
+
+NTSTATUS
+LsarCreateTrustedDomain(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_TRUST_INFORMATION TrustedDomainInformation,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE TrustedDomainHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaCreateTrustedDomain API.
+
+ The LsaCreateTrustedDomain API creates a new TrustedDomain object. The
+ caller must have POLICY_TRUST_ADMIN access to the Policy Object.
+
+ Note that NO verification is done to check that the given domain name
+ matches the given SID or that the SID or name represent an actual domain.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainInformation - Pointer to structure containing the name and
+ SID of the new Trusted Domain.
+
+ DesiredAccess - Specifies the accesses to be granted for the newly
+ created object.
+
+ TrustedDomainHandle - receives a handle referencing the newly created
+ object. This handle is used on subsequent accesses to the object.
+
+--*/
+
+{
+ NTSTATUS Status, SavedStatus, SecondaryStatus;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_DOMAIN];
+ PLSAP_DB_ATTRIBUTE NextAttribute;
+ UNICODE_STRING LogicalNameU;
+ BOOLEAN InternalPolicyHandleReferenced = FALSE;
+ BOOLEAN ClientPolicyHandleReferenced = FALSE;
+ BOOLEAN AttributeBuffersAllocated = FALSE;
+ PSID DomainSid;
+ ULONG AttributeCount = 0;
+ LSAP_DB_HANDLE InternalTrustedDomainHandle = NULL;
+ PVOID TrustedDomainNameAttributeValue = NULL;
+ ULONG TrustedDomainPosixOffset, NextTrustedDomainPosixOffset, TrustedDomainPosixOffsetLength;
+ PPOLICY_LSA_SERVER_ROLE_INFO PolicyServerRoleInfo = NULL;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ BOOLEAN BooleanStatus = FALSE;
+ LogicalNameU.Length = 0;
+
+ Status = STATUS_INVALID_PARAMETER;
+ SecondaryStatus = STATUS_SUCCESS;
+
+ if (!ARGUMENT_PRESENT( TrustedDomainInformation )) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ DomainSid = TrustedDomainInformation->Sid;
+
+ //
+ // Validate the Trusted Domain Sid.
+ //
+
+ if (!RtlValidSid( DomainSid )) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the PolicyHandle
+ // is valid and has the necessary access granted. Reference the Policy
+ // Object handle (as container object).
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_TRUST_ADMIN,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ ClientPolicyHandleReferenced = TRUE;
+
+ //
+ // Construct the Trusted Domain Name attribute info.
+ //
+
+ NextAttribute = Attributes;
+
+ Status = LsapDbMakeUnicodeAttribute(
+ (PUNICODE_STRING) &TrustedDomainInformation->Name,
+ &LsapDbNames[TrDmName],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ AttributeBuffersAllocated = TRUE;
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Construct the Trusted Domain Sid attribute info
+ //
+
+ DomainSid = TrustedDomainInformation->Sid,
+
+ //
+ // If no Sid was specified, return an error. Note that RPC will
+ // not catch this case.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (DomainSid == NULL) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ Status = LsapDbMakeSidAttribute(
+ DomainSid,
+ &LsapDbNames[Sid],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Set the Posix Offset for this Trusted Domain.
+ //
+ // The rules are as follows:
+ //
+ // For a PDC, set the Posix Offset to the next seed value. This value is
+ // kept as the PolNxPxF attribute in the Policy Object
+ //
+ // For a BDC, set the Posix Offset to the null Posix offset. It will be
+ // set by the Replicator
+ //
+
+ TrustedDomainPosixOffsetLength = sizeof(ULONG);
+ TrustedDomainPosixOffset = SE_NULL_POSIX_OFFSET;
+
+ //
+ // We allow this API to be called before we're completely initialized
+ // for installation reasons. However, it is the responsibility of the
+ // installation program to not call it before the Product Type is
+ // obtainable from the Registry.
+ //
+
+ if (!LsapDbIsServerInitialized()) {
+
+ BooleanStatus = RtlGetNtProductType(&LsapProductType);
+
+ if (!BooleanStatus) {
+
+ goto CreateTrustedDomainError;
+ }
+ }
+
+ if ((LsapProductType == NtProductWinNt) ||
+ (LsapProductType == NtProductServer)) {
+
+ //
+ // We're a Workstation. The only Trusted Domain object on a
+ // Workstation is the one for its Primary Domain (if any).
+ // Compare the Sid against the Sid in the Policy Primary Domain
+ // Information. If the latter Sid matches or is NULL (because
+ // we're installing or joining a domain from a workgroup configuration)
+ // assume that this Trusted Domain object will be for the Primary
+ // Domain. If the Sid does not match, set the Posix Offset to the
+ // NULL Posix Offset as that Domain will not be accessible from the
+ // Win NT system.
+ //
+
+ TrustedDomainPosixOffset = SE_PRIMARY_DOMAIN_POSIX_OFFSET;
+
+ Status = LsapDbQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ //
+ // We read the Primary Domain name and Sid. If the Sid is non-NULL
+ // but does not match the Sid specified for the Trusted Domain
+ // being created, set the Posix Offset to the NULL Posix Offset.
+ //
+
+ if (PolicyPrimaryDomainInfo->Sid != NULL) {
+
+ if (!RtlEqualSid(
+ (PSID) PolicyPrimaryDomainInfo->Sid,
+ TrustedDomainInformation->Sid
+ )) {
+
+ TrustedDomainPosixOffset = SE_NULL_POSIX_OFFSET;
+ }
+ }
+
+ } else if (LsapProductType == NtProductLanManNt) {
+
+ Status = LsapDbQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyLsaServerRoleInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyServerRoleInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ if (PolicyServerRoleInfo->LsaServerRole == PolicyServerRolePrimary) {
+
+ //
+ // Acquire the Lsa Database lock. Reference the handle and start
+ // an Lsa Database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ LsapPolicyHandle,
+ (ACCESS_MASK) 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION | LSAP_DB_TRUSTED
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ InternalPolicyHandleReferenced = TRUE;
+
+ //
+ // Get the next Posix Offset value to be used. This is stored
+ // as a non-cached attribute of the Policy Object.
+ //
+
+
+ Status = LsapDbReadAttributeObject(
+ LsapPolicyHandle,
+ &LsapDbNames[PolNxPxF],
+ &TrustedDomainPosixOffset,
+ &TrustedDomainPosixOffsetLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If we failed other than becuase the attribute was not
+ // found, set the value to the initial seed. This allows
+ // the LSA to work on old Policy Databases.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+ TrustedDomainPosixOffset = SE_INITIAL_TRUSTED_DOMAIN_POSIX_OFFSET;
+ }
+
+ NextTrustedDomainPosixOffset = TrustedDomainPosixOffset;
+
+ if (NextTrustedDomainPosixOffset == SE_MAX_TRUSTED_DOMAIN_POSIX_OFFSET) {
+
+ NextTrustedDomainPosixOffset = SE_INITIAL_TRUSTED_DOMAIN_POSIX_OFFSET;
+
+ } else {
+
+ NextTrustedDomainPosixOffset += SE_TRUSTED_DOMAIN_POSIX_OFFSET_INCR;
+ }
+
+ //
+ // Write the updated next Posix Offset to be given out back to
+ // the Policy Object.
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ LsapPolicyHandle,
+ &LsapDbNames[PolNxPxF],
+ &NextTrustedDomainPosixOffset,
+ TrustedDomainPosixOffsetLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ //
+ // Apply the transaction to update the next Posix Offset in the
+ // Policy object. No need to inform the Replicator since
+ // this value is not replicated (Posix Offsets are explicitly
+ // set on BDC's by the replicator (via LsarSetInformationTrustedDomain()).
+ //
+
+ Status = LsapDbDereferenceObject(
+ &LsapPolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK |
+ LSAP_DB_FINISH_TRANSACTION |
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ InternalPolicyHandleReferenced = FALSE;
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto CreateTrustedDomainError;
+ }
+ }
+
+ } else {
+
+ Status = STATUS_INTERNAL_DB_CORRUPTION;
+ goto CreateTrustedDomainError;
+ }
+
+ //
+ // Add a transaction to write the Posix Offset to the Trusted Domain
+ // object when it is created.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[TrDmPxOf],
+ &TrustedDomainPosixOffset,
+ TrustedDomainPosixOffsetLength,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Construct the Logical Name (Internal LSA Database Name) of the
+ // Trusted Domain object. The Logical Name is constructed from the Domain
+ // Sid by extracting the Relative Id (lowest subauthority) and converting
+ // it to an 8-digit numeric Unicode String in which leading zeros are
+ // added if needed.
+ //
+
+ Status = LsapDbSidToLogicalNameObject( DomainSid, &LogicalNameU );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ //
+ // Fill in the ObjectInformation structure. Initialize the
+ // embedded Object Attributes with the PolicyHandle as the
+ // Root Directory (Container Object) handle and the Logical Name (Rid)
+ // of the Trusted Domain. Store the types of the object and its container.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ &LogicalNameU,
+ OBJ_CASE_INSENSITIVE,
+ PolicyHandle,
+ NULL
+ );
+
+ ObjectInformation.ObjectTypeId = TrustedDomainObject;
+ ObjectInformation.ContainerTypeId = PolicyObject;
+ ObjectInformation.Sid = DomainSid;
+
+ //
+ // Create the Trusted Domain Object. We fail if the object already exists.
+ // Note that the object create routine performs a Database transaction.
+ //
+
+ Status = LsapDbCreateObject(
+ &ObjectInformation,
+ DesiredAccess,
+ LSAP_DB_OBJECT_CREATE,
+ 0,
+ Attributes,
+ AttributeCount,
+ TrustedDomainHandle
+ );
+
+ //
+ // If object creation failed, dereference the container object.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ //
+ // Add the Trusted Domain to the Trusted Domain List.
+ //
+
+ Status = LsapDbInsertTrustedDomainList(
+ (ULONG) 1,
+ TrustedDomainInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateTrustedDomainError;
+ }
+
+ if (LsapAdtAuditingPolicyChanges()) {
+
+ SavedStatus = Status;
+
+ InternalTrustedDomainHandle = (LSAP_DB_HANDLE) *TrustedDomainHandle;
+
+ Status = LsapAdtGenerateLsaAuditEvent(
+ TrustedDomainHandle,
+ SE_CATEGID_POLICY_CHANGE,
+ SE_AUDITID_TRUSTED_DOMAIN_ADD,
+ NULL,
+ 1,
+ (PSID) &InternalTrustedDomainHandle->Sid,
+ 1,
+ (PUNICODE_STRING) &TrustedDomainInformation->Name,
+ NULL
+ );
+
+ //
+ // Ignore failure status from auditing.
+ //
+
+ Status = SavedStatus;
+ }
+
+ //
+ // If necessary, release the LSA Database lock. Note that we don't
+ // call LsapDbDereferenceObject() because we want to leave the
+ // reference count incremented by default in this success case.
+ // In the error case, we call LsapDbDereferenceObject().
+ //
+
+ if (ClientPolicyHandleReferenced) {
+
+ LsapDbReleaseLock();
+ ClientPolicyHandleReferenced = FALSE;
+ }
+
+CreateTrustedDomainFinish:
+
+ //
+ // If necessary, free the Policy Lsa Server Role Information
+ //
+
+ if (PolicyServerRoleInfo != NULL) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyLsaServerRoleInformation,
+ (PLSAPR_POLICY_INFORMATION) PolicyServerRoleInfo
+ );
+
+ PolicyServerRoleInfo = NULL;
+ }
+
+ //
+ // If necessary, free the Policy Primary Domain Information
+ //
+
+ if (PolicyPrimaryDomainInfo != NULL) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyPrimaryDomainInformation,
+ (PLSAPR_POLICY_INFORMATION) PolicyPrimaryDomainInfo
+ );
+
+ PolicyPrimaryDomainInfo = NULL;
+ }
+
+
+ //
+ // If necessary, dereference the Internal Policy Handle.
+ //
+
+ if (InternalPolicyHandleReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &LsapPolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ InternalPolicyHandleReferenced = FALSE;
+ }
+
+ //
+ // Free any Attribute Value buffers allocated.
+ //
+
+ if (AttributeBuffersAllocated) {
+
+ SecondaryStatus = LsapDbFreeAttributes( AttributeCount, Attributes );
+
+ AttributeBuffersAllocated = FALSE;
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ goto CreateTrustedDomainError;
+ }
+ }
+
+ //
+ // If necessary, free the Unicode String buffer allocated for the
+ // Logical Name.
+ //
+
+ if ( LogicalNameU.Length > 0 ) {
+
+ RtlFreeUnicodeString(&LogicalNameU);
+ LogicalNameU.Length = 0;
+ }
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (*TrustedDomainHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+ return(Status);
+
+CreateTrustedDomainError:
+
+ //
+ // If necessary, dereference the client Policy Handle and release the
+ // LSA Database lock.
+ //
+
+ if (ClientPolicyHandleReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ ClientPolicyHandleReferenced = FALSE;
+ }
+
+ if (NT_SUCCESS(Status) && !NT_SUCCESS(SecondaryStatus)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto CreateTrustedDomainFinish;
+}
+
+
+NTSTATUS
+LsarOpenTrustedDomain(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID TrustedDomainSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE TrustedDomainHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaOpenTrustedDomain API opens an existing TrustedDomain object
+ using the SID as the primary key value.
+
+Arguments:
+
+ PolicyHandle - An open handle to a Policy object.
+
+ TrustedDomainSid - Pointer to the account's Sid.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested to the target object.
+
+ TrustedDomainHandle - Receives a handle to be used in future requests.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_TRUSTED_DOMAIN_NOT_FOUND - There is no TrustedDomain object in the
+ target system's LSA Database having the specified AccountSid.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Call the internal routine. Caller is not trusted and the Database
+ // lock needs to be acquired.
+ //
+
+ Status = LsapDbOpenTrustedDomain(
+ PolicyHandle,
+ (PSID) TrustedDomainSid,
+ DesiredAccess,
+ TrustedDomainHandle,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (*TrustedDomainHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbOpenTrustedDomain(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE TrustedDomainHandle,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens a Trusted Domain Object, optionally with
+ trusted access.
+
+Arguments:
+
+ PolicyHandle - An open handle to a Policy object.
+
+ TrustedDomainSid - Pointer to the account's Sid.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested to the target object.
+
+ TrustedDomainHandle - Receives a handle to be used in future requests.
+
+ Options - Specifies option flags
+
+ LSAP_DB_ACQUIRE_LOCK - Acquire the Lsa Database lock for the
+ duration of the open operation.
+
+ LSAP_DB_TRUSTED - Always generate a Trusted Handle to the opened
+ object. If not specified, the trust status of the returned
+ handle is inherited from the PolicyHandle as container object.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_TRUSTED_DOMAIN_NOT_FOUND - There is no TrustedDomain object in the
+ target system's LSA Database having the specified AccountSid.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ UNICODE_STRING LogicalNameU;
+ BOOLEAN ContainerReferenced = FALSE;
+ BOOLEAN AcquiredLock = FALSE;
+
+ //
+ // Validate the Trusted Domain Sid.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!RtlValidSid( TrustedDomainSid )) {
+
+ goto OpenTrustedDomainError;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle
+ // (container object handle) is valid, and is of the expected type.
+ // Reference the container object handle. This reference remains in
+ // effect until the child object handle is closed.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ 0,
+ PolicyObject,
+ Options
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenTrustedDomainError;
+ }
+
+ ContainerReferenced =TRUE;
+
+ if (Options & LSAP_DB_ACQUIRE_LOCK) {
+
+ AcquiredLock = TRUE;
+ }
+
+ //
+ // Setup Object Information prior to calling the Object
+ // Open routine. The Object Type, Container Object Type and
+ // Logical Name (derived from the Sid) need to be filled in.
+ //
+
+ ObjectInformation.ObjectTypeId = TrustedDomainObject;
+ ObjectInformation.ContainerTypeId = PolicyObject;
+ ObjectInformation.Sid = TrustedDomainSid;
+
+ //
+ // Construct the Logical Name of the Trusted Domain object. The Logical
+ // Name is constructed from the Trusted Domain Sid by extracting the
+ // Relative Id (lowest subauthority) and converting it to an 8-digit
+ // numeric Unicode String in which leading zeros are added if needed.
+ //
+
+ Status = LsapDbSidToLogicalNameObject( TrustedDomainSid, &LogicalNameU );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenTrustedDomainError;
+ }
+
+ //
+ // Initialize the Object Attributes. The Container Object Handle and
+ // Logical Name (Internal Name) of the object must be set up.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ &LogicalNameU,
+ 0,
+ PolicyHandle,
+ NULL
+ );
+
+ //
+ // Open the specific Trusted Domain object. Note that the
+ // handle returned is an RPC Context Handle.
+ //
+
+ Status = LsapDbOpenObject(
+ &ObjectInformation,
+ DesiredAccess,
+ Options,
+ TrustedDomainHandle
+ );
+
+ RtlFreeUnicodeString( &LogicalNameU );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenTrustedDomainError;
+ }
+
+OpenTrustedDomainFinish:
+
+ //
+ // If necessary, release the LSA Database lock. Note that object
+ // remains referenced unless we came here via error.
+ //
+
+ if (AcquiredLock) {
+
+ LsapDbReleaseLock();
+ }
+
+ return( Status );
+
+OpenTrustedDomainError:
+
+ //
+ // If necessary, dereference the Container Object handle. Note that
+ // this is only done in the error case. In the non-error case, the
+ // Container handle stays referenced until the TrustedDomain object is
+ // closed.
+ //
+
+ if (ContainerReferenced) {
+
+ *TrustedDomainHandle = NULL;
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ 0,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ goto OpenTrustedDomainFinish;
+}
+
+
+NTSTATUS
+LsarQueryInfoTrustedDomain(
+ IN LSAPR_HANDLE TrustedDomainHandle,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_TRUSTED_DOMAIN_INFO *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaQueryInfoTrustedDomain API.
+
+ The LsaQueryInfoTrustedDomain API obtains information from a
+ TrustedDomain object. The caller must have access appropriate to the
+ information being requested (see InformationClass parameter).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ TrustedAccountNameInformation TRUSTED_QUERY_ACCOUNT_NAME
+ TrustedControllersInformation TRUSTED_QUERY_CONTROLLERS
+ TrustedPosixInformation TRUSTED_QUERY_POSIX
+
+ Buffer - Receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status, ReadAttributesStatus;
+
+ PTRUSTED_DOMAIN_NAME_INFO TrustedDomainNameInfo;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo;
+ PTRUSTED_POSIX_OFFSET_INFO TrustedPosixOffsetInfo;
+
+ BOOLEAN ObjectReferenced = FALSE;
+
+ ACCESS_MASK DesiredAccess;
+ ULONG AttributeCount = 0;
+ ULONG AttributeNumber = 0;
+ PVOID InformationBuffer = NULL;
+ LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_INFO_CLASS_DOMAIN];
+ PLSAP_DB_ATTRIBUTE NextAttribute;
+ BOOLEAN InfoBufferInAttributeArray = TRUE;
+ ULONG TrustedPosixOffset = 0;
+
+ //
+ // Validate the Information Class and determine the access required to
+ // query this Trusted Domain Information Class.
+ //
+
+ Status = LsapDbVerifyInfoQueryTrustedDomain(
+ InformationClass,
+ FALSE,
+ &DesiredAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the handle is a valid
+ // handle to a TrustedDomain object and has the necessary access granted.
+ // Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ TrustedDomainHandle,
+ DesiredAccess,
+ TrustedDomainObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Compile a list of the attributes that hold the Trusted Domain Information of
+ // the specified class.
+ //
+
+ NextAttribute = Attributes;
+
+ switch (InformationClass) {
+
+ case TrustedDomainNameInformation:
+
+ //
+ // Request read of the Trusted Account Name Information.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[TrDmName],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ AttributeCount++;
+ break;
+
+ case TrustedControllersInformation:
+
+ //
+ // Request read of the Trusted Controllers Information.
+ // intermediate buffer.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[TrDmCtN],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ AttributeCount++;
+ break;
+
+ case TrustedPosixOffsetInformation:
+
+ //
+ // Request read of the Trusted Posix Offset Information.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[TrDmPxOf],
+ &TrustedPosixOffset,
+ sizeof(ULONG),
+ FALSE
+ );
+
+ AttributeCount++;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ //
+ //
+ // Read the attributes corresponding to the given Policy Information
+ // Class. Memory will be allocated where required via MIDL_user_allocate
+ // for attribute values.
+ //
+
+ Status = LsapDbReadAttributesObject(
+ TrustedDomainHandle,
+ Attributes,
+ AttributeCount
+ );
+
+ ReadAttributesStatus = Status;
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If the error was that one or more of the attributes holding
+ // the information of the given class was not found, continue.
+ // Otherwise, return an error.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+ }
+
+ //
+ // Now copy the information read to the output. For certain information
+ // classes where the information is stored as the value of a single
+ // attribute of the Policy object and is in the form required by the
+ // caller, we can just return the pointer to this buffer. For all
+ // other cases, an output buffer structure tree of the form desired
+ // must be allocated via MIDL_user_allocate() and the information read from the attribute(s) of
+ // the Policy object must be copied in. These buffers must then be freed
+ // by this routine before exit. The array of attribute information
+ // filled in by LsapDbReadAttributes() has MemoryAllocated = TRUE
+ // in all cases. We reset this flag to FALSE in the simple cases where
+ // we can use the buffer as is. The Finish section of the routine
+ // will free up any buffers referenced by the AttributeValue pointer
+ // in the attribute array where MemoryAllocated is still TRUE. If
+ // we go to error, the error processing is responsible for freeing
+ // those buffers which would be passed to the calling RPC server stub
+ // in the non-error case.
+ //
+
+ NextAttribute = Attributes;
+
+ switch (InformationClass) {
+
+ case TrustedDomainNameInformation:
+
+ //
+ // Allocate memory for output buffer top-level structure.
+ //
+
+ TrustedDomainNameInfo =
+ MIDL_user_allocate(sizeof(TRUSTED_DOMAIN_NAME_INFO));
+
+ if (TrustedDomainNameInfo == NULL) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ InfoBufferInAttributeArray = FALSE;
+
+ //
+ // Copy the Unicode Name field to the output. Original buffer will
+ // be freed in Finish section.
+ //
+
+ Status = LsapDbCopyUnicodeAttribute(
+ &TrustedDomainNameInfo->Name,
+ NextAttribute,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ InformationBuffer = TrustedDomainNameInfo;
+ NextAttribute++;
+ break;
+
+ case TrustedControllersInformation:
+
+ //
+ // Allocate memory for output buffer top-level structure.
+ //
+
+ TrustedControllersInfo =
+ MIDL_user_allocate(sizeof(TRUSTED_CONTROLLERS_INFO));
+
+ if (TrustedControllersInfo == NULL) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ RtlZeroMemory( TrustedControllersInfo, sizeof(TRUSTED_CONTROLLERS_INFO) );
+
+ InfoBufferInAttributeArray = FALSE;
+
+ //
+ // Copy the Trusted Controllers Info to the output.
+ //
+
+ Status = LsapDbCopyMultiUnicodeAttribute(
+ NextAttribute,
+ (PULONG) &TrustedControllersInfo->Entries,
+ (PUNICODE_STRING *) &TrustedControllersInfo->Names
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ InformationBuffer = TrustedControllersInfo;
+ break;
+
+ case TrustedPosixOffsetInformation:
+
+ //
+ // Allocate memory for top-level output buffer.
+ //
+
+ InformationBuffer = NextAttribute->AttributeValue;
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ TrustedPosixOffsetInfo = MIDL_user_allocate(sizeof(TRUSTED_POSIX_OFFSET_INFO));
+
+ if (TrustedPosixOffsetInfo == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ InfoBufferInAttributeArray = FALSE;
+
+ //
+ // Copy Posix Offset value to output.
+ //
+
+ TrustedPosixOffsetInfo->Offset = TrustedPosixOffset;
+
+ InformationBuffer = TrustedPosixOffsetInfo;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoTrustedDomainError;
+ }
+
+ //
+ // Verify that the returned Trusted Domain Information is valid. If not,
+ // the Policy Database is corrupt.
+ //
+
+ if (!LsapDbValidInfoTrustedDomain(InformationClass, InformationBuffer)) {
+
+ Status = STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ //
+ // Return a pointer to the output buffer to the caller
+ //
+
+ *Buffer = (PLSAPR_TRUSTED_DOMAIN_INFO) InformationBuffer;
+
+QueryInfoTrustedDomainFinish:
+
+ //
+ // Free any unwanted buffers that were allocated by
+ // LsapDbReadAttributesObject() and that are not being returned to the
+ // caller server stub. The server stub will free the buffers that we
+ // do return after copying them to the return RPC transmit buffer.
+ //
+
+ for (NextAttribute = Attributes, AttributeNumber = 0;
+ AttributeNumber < AttributeCount;
+ NextAttribute++, AttributeNumber++) {
+
+ //
+ // If buffer holding attribute is marked as allocated, it is
+ // to be freed here.
+ //
+
+ if (NextAttribute->MemoryAllocated) {
+
+ if (NextAttribute->AttributeValue != NULL) {
+
+ MIDL_user_free(NextAttribute->AttributeValue);
+ NextAttribute->AttributeValue = NULL;
+ NextAttribute->MemoryAllocated = FALSE;
+ }
+ }
+ }
+
+ //
+ // If necessary, dereference the Trusted Domain Object, release the LSA Database lock and
+ // return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &TrustedDomainHandle,
+ TrustedDomainObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+QueryInfoTrustedDomainError:
+
+ //
+ // If necessary, free the memory allocated for the output buffer.
+ // We only do this free if the buffer is not referenced by the
+ // attribute array, since all buffers so referenced will be freed
+ // here or in the Finish section.
+ //
+
+ if ((InformationBuffer != NULL) && !InfoBufferInAttributeArray) {
+
+ MIDL_user_free(InformationBuffer);
+ InformationBuffer = NULL;
+ }
+
+ //
+ // Free the buffers referenced by the attributes array that will not be
+ // freed by the Finish section of this routine.
+ //
+
+ for (NextAttribute = Attributes, AttributeNumber = 0;
+ AttributeNumber < AttributeCount;
+ NextAttribute++, AttributeNumber++) {
+
+ //
+ // If buffer holding attribute is marked as normally not to be freed,
+ // will not get freed by the Finish section so it must be freed here.
+ //
+
+ if (!NextAttribute->MemoryAllocated) {
+
+ if (NextAttribute->AttributeValue != NULL) {
+
+ MIDL_user_free(NextAttribute->AttributeValue);
+ NextAttribute->AttributeValue = NULL;
+ NextAttribute->MemoryAllocated = FALSE;
+ }
+
+ NextAttribute->MemoryAllocated = FALSE;
+ }
+ }
+
+ goto QueryInfoTrustedDomainFinish;
+}
+
+
+NTSTATUS
+LsarSetInformationTrustedDomain(
+ IN LSAPR_HANDLE TrustedDomainHandle,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaSetInfoTrustedDomain API.
+
+ The LsaSetInformationTrustedDomain API modifies information in the Trusted
+ Domain Object. The caller must have access appropriate to the
+ information to be changed in the Policy Object, see the InformationClass
+ parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationClass - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ TrustedDomainNameInformation ( Cannot be set )
+ TrustedControllersInformation TRUSTED_SET_CONTROLLERS
+ TrustedPosixOffsetInformation TRUSTED_POSIX_INFORMATION
+
+ Buffer - Points to a structure containing the information appropriate
+ to the InformationClass parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+
+{
+ NTSTATUS Status;
+ ACCESS_MASK DesiredAccess;
+
+ BOOLEAN ObjectReferenced = FALSE;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo;
+ PTRUSTED_POSIX_OFFSET_INFO TrustedPosixOffsetInfo;
+
+ LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_INFO_CLASS_DOMAIN];
+ PLSAP_DB_ATTRIBUTE NextAttribute;
+ ULONG AttributeCount = 0;
+ ULONG AttributeNumber;
+ ULONG TrustedDomainPosixOffset;
+ ULONG NextTrustedDomainPosixOffset;
+ ULONG TrustedDomainPosixOffsetLength;
+ BOOLEAN InternalPolicyHandleReferenced = FALSE;
+
+ //
+ // Validate the Information Class and Trusted Domain Information provided and
+ // if valid, return the mask of accesses required to update this
+ // class of Trusted Domain information.
+ //
+
+ Status = LsapDbVerifyInfoSetTrustedDomain(
+ InformationClass,
+ TrustedDomainInformation,
+ FALSE,
+ &DesiredAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the handle is
+ // valid, is a handle to a TrustedDomain Object and has the necessary accesses
+ // granted. Reference the handle and start an Lsa Database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ TrustedDomainHandle,
+ DesiredAccess,
+ TrustedDomainObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Update the specified information in the Policy Object.
+ //
+
+ NextAttribute = Attributes;
+
+ switch (InformationClass) {
+
+ case TrustedDomainNameInformation:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ case TrustedControllersInformation:
+
+ TrustedControllersInfo = (PTRUSTED_CONTROLLERS_INFO) TrustedDomainInformation;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[TrDmCtEn],
+ &TrustedControllersInfo->Entries,
+ sizeof(ULONG),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ Status = LsapDbMakeMultiUnicodeAttribute(
+ NextAttribute,
+ &LsapDbNames[TrDmCtN],
+ TrustedControllersInfo->Names,
+ TrustedControllersInfo->Entries
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case TrustedPosixOffsetInformation:
+
+ TrustedPosixOffsetInfo = (PTRUSTED_POSIX_OFFSET_INFO) TrustedDomainInformation;
+
+ //
+ // If we are setting the posix offset, then we need to make sure
+ // to adjust the next posix offset upward to be greater than this
+ // posix offset.
+ //
+
+
+ //
+ // Acquire the Lsa Database lock. Reference the handle and start
+ // an Lsa Database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ LsapPolicyHandle,
+ (ACCESS_MASK) 0,
+ PolicyObject,
+ LSAP_DB_TRUSTED
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+ InternalPolicyHandleReferenced = TRUE;
+
+
+ //
+ // Get the next Posix Offset value to be used. This is stored
+ // as a non-cached attribute of the Policy Object.
+ //
+
+ TrustedDomainPosixOffsetLength = sizeof(ULONG);
+
+ Status = LsapDbReadAttributeObject(
+ LsapPolicyHandle,
+ &LsapDbNames[PolNxPxF],
+ &TrustedDomainPosixOffset,
+ &TrustedDomainPosixOffsetLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If we failed other than becuase the attribute was not
+ // found, set the value to the initial seed. This allows
+ // the LSA to work on old Policy Databases.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+ TrustedDomainPosixOffset = SE_INITIAL_TRUSTED_DOMAIN_POSIX_OFFSET;
+ }
+
+ //
+ // Set the next posix offset to be either the current next or
+ // the new offset incremented, and possibly rolled over.
+ //
+
+ if (TrustedDomainPosixOffset > TrustedPosixOffsetInfo->Offset) {
+ NextTrustedDomainPosixOffset = TrustedDomainPosixOffset;
+ } else {
+
+ NextTrustedDomainPosixOffset = TrustedPosixOffsetInfo->Offset;
+ if (NextTrustedDomainPosixOffset == SE_MAX_TRUSTED_DOMAIN_POSIX_OFFSET) {
+
+ NextTrustedDomainPosixOffset = SE_INITIAL_TRUSTED_DOMAIN_POSIX_OFFSET;
+
+ } else {
+
+ NextTrustedDomainPosixOffset += SE_TRUSTED_DOMAIN_POSIX_OFFSET_INCR;
+ }
+
+ }
+
+ //
+ // Write the updated next Posix Offset to be given out back to
+ // the Policy Object.
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ LsapPolicyHandle,
+ &LsapDbNames[PolNxPxF],
+ &NextTrustedDomainPosixOffset,
+ TrustedDomainPosixOffsetLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+ //
+ // Apply the transaction to update the next Posix Offset in the
+ // Policy object. No need to inform the Replicator since
+ // this value is not replicated (Posix Offsets are explicitly
+ // set on BDC's by the replicator (via LsarSetInformationTrustedDomain()).
+ //
+
+ Status = LsapDbDereferenceObject(
+ &LsapPolicyHandle,
+ PolicyObject,
+ 0, // no flags
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ InternalPolicyHandleReferenced = FALSE;
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[TrDmPxOf],
+ &TrustedPosixOffsetInfo->Offset,
+ sizeof(ULONG),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto SetInfoTrustedDomainError;
+ break;
+ }
+
+ //
+ // Update the TrustedDomain Object attributes
+
+ Status = LsapDbWriteAttributesObject(
+ TrustedDomainHandle,
+ Attributes,
+ AttributeCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInfoTrustedDomainError;
+ }
+
+SetInfoTrustedDomainFinish:
+
+
+ if (InternalPolicyHandleReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &LsapPolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ InternalPolicyHandleReferenced = FALSE;
+ }
+
+ //
+ // Free memory allocated by this routine for attribute buffers.
+ // These have MemoryAllocated = TRUE in their attribute information.
+ // Leave alone buffers allocated by calling RPC stub.
+ //
+
+ for( NextAttribute = Attributes, AttributeNumber = 0;
+ AttributeNumber < AttributeCount;
+ NextAttribute++, AttributeNumber++) {
+
+ if (NextAttribute->MemoryAllocated) {
+
+ ASSERT(NextAttribute->AttributeValue != NULL);
+ MIDL_user_free(NextAttribute->AttributeValue);
+ }
+ }
+
+ //
+ // If necessary, dereference the Trusted Domain Object, release the LSA Database lock and
+ // return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &TrustedDomainHandle,
+ TrustedDomainObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbChange,
+ Status
+ );
+ }
+
+ return(Status);
+
+SetInfoTrustedDomainError:
+
+ goto SetInfoTrustedDomainFinish;
+ return(Status);
+}
+
+
+NTSTATUS
+LsarEnumerateTrustedDomains(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaEnumerateTrustedDomains API.
+
+ The LsaEnumerateTrustedDomains API returns information about
+ TrustedDomain objects. This call requires POLICY_VIEW_LOCAL_INFORMATION
+ access to the Policy object. Since there may be more information than
+ can be returned in a single call of the routine, multiple calls can be
+ made to get all of the information. To support this feature, the caller
+ is provided with a handle that can be used across calls to the API. On
+ the initial call, EnumerationContext should point to a variable that has
+ been initialized to 0. On each subsequent call, the value returned by
+ the preceding call should be passed in unchanged. The enumeration is
+ complete when the warning STATUS_NO_MORE_ENTRIES is returned.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationBuffer - Pointer to an enumeration structure that will receive
+ a count of the Trusted Domains enumerated on this call and a pointer to
+ an array of entries containing information for each enumerated
+ Trusted Domain.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if no objects have been enumerated because the
+ EnumerationContext value is too high.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLSA_TRUST_INFORMATION DomainTrustInfo = NULL;
+ PLSA_TRUST_INFORMATION NextDomainTrustInfo = NULL;
+ PSID *Sids = NULL;
+ LSAP_DB_ATTRIBUTE DomainNameAttribute;
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+ ULONG MaxLength;;
+
+ DomainNameAttribute.AttributeValue = NULL;
+
+ //
+ // If no Enumeration Structure is provided, return an error.
+ //
+
+ if (!ARGUMENT_PRESENT(EnumerationBuffer)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Limit the enumeration length except for trusted callers
+ //
+
+ if ( !((LSAP_DB_HANDLE)PolicyHandle)->Trusted &&
+ (PreferedMaximumLength > LSA_MAXIMUM_ENUMERATION_LENGTH)
+ ) {
+ MaxLength = LSA_MAXIMUM_ENUMERATION_LENGTH;
+ } else {
+ MaxLength = PreferedMaximumLength;
+ }
+
+
+ //
+ // Call worker.
+ //
+
+ Status = LsapDbEnumerateTrustedDomains(
+ LsapPolicyHandle,
+ EnumerationContext,
+ EnumerationBuffer,
+ MaxLength
+ );
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ }
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsapDbEnumerateTrustedDomains(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs an enumeration of the Trusted Domains,
+ using the Trusted Domain List if avaliable, or backing storage.
+
+ This routine is called internally by the LSA only. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information. To
+ support this feature, the caller is provided with a handle that can
+ be used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationBuffer - Pointer to an enumeration structure that will receive
+ a count of the Trusted Domains enumerated on this call and a pointer to
+ an array of entries containing information for each enumerated
+ Trusted Domain.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if there are no more objects to enumerate. Note that
+ one or more objects may be enumerated on a call that returns this
+ reply.
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // If no Enumeration Structure is provided, return an error.
+ //
+
+
+ if (!ARGUMENT_PRESENT(EnumerationBuffer)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Use the cache if available.
+ //
+
+ if (LsapDbIsCacheValid(TrustedDomainObject)) {
+
+ Status = LsapDbEnumerateTrustedDomainList(
+ NULL,
+ EnumerationContext,
+ EnumerationBuffer,
+ PreferedMaximumLength
+ );
+
+
+ } else {
+
+ //
+ // Use slow method of enumeration, by accessing backing storage.
+ // Later, we'll implement rebuilding of the cache.
+
+ Status = LsapDbSlowEnumerateTrustedDomains(
+ PolicyHandle,
+ EnumerationContext,
+ EnumerationBuffer,
+ PreferedMaximumLength
+ );
+ }
+
+
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsapDbSlowEnumerateTrustedDomains(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs the same actions as LsarEnumerateTrustedDomains()
+ except that the Trusted Domain List is not used.
+
+ This routine is called internally by the LSA only. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information. To
+ support this feature, the caller is provided with a handle that can
+ be used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationBuffer - Pointer to an enumeration structure that will receive
+ a count of the Trusted Domains enumerated on this call and a pointer to
+ an array of entries containing information for each enumerated
+ Trusted Domain.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if there are no more objects to enumerate. Note that
+ one or more objects may be enumerated on a call that returns this
+ reply.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ LSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer;
+ PLSA_TRUST_INFORMATION DomainTrustInfo = NULL;
+ PLSA_TRUST_INFORMATION NextDomainTrustInfo = NULL;
+ PSID *Sids = NULL;
+ LSAP_DB_ATTRIBUTE DomainNameAttribute;
+ ULONG DomainTrustInfoLength;
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+ ULONG EntriesRead;
+ BOOLEAN ObjectReferenced = FALSE;
+
+ DomainNameAttribute.AttributeValue = NULL;
+
+ //
+ // If no Enumeration Structure is provided, return an error.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!ARGUMENT_PRESENT(EnumerationBuffer)) {
+
+ goto SlowEnumerateTrustedDomainsError;
+ }
+
+ //
+ // Initialize the internal Lsa Database Enumeration Buffer, and
+ // the provided Enumeration Buffer to NULL.
+ //
+
+ DbEnumerationBuffer.EntriesRead = 0;
+ DbEnumerationBuffer.Sids = NULL;
+ EnumerationBuffer->EntriesRead = 0;
+ EnumerationBuffer->Information = NULL;
+ DomainNameAttribute.AttributeValue = NULL;
+
+ //
+ // Call general Sid enumeration routine. This will return an array
+ // of pointers to Sids of Trusted Domains referenced from the
+ // Enumeration Buffer.
+ //
+
+ Status = LsapDbEnumerateSids(
+ PolicyHandle,
+ TrustedDomainObject,
+ EnumerationContext,
+ &DbEnumerationBuffer,
+ PreferedMaximumLength
+ );
+
+ if ((Status != STATUS_NO_MORE_ENTRIES) && !NT_SUCCESS(Status)) {
+
+ goto SlowEnumerateTrustedDomainsError;
+ }
+
+ //
+ // Return the number of entries read. Note that the Enumeration Buffer
+ // returned from LsapDbEnumerateSids is expected to be non-null
+ // in all non-error cases.
+ //
+
+ EntriesRead = DbEnumerationBuffer.EntriesRead;
+
+ if (EntriesRead == 0) {
+
+ goto SlowEnumerateTrustedDomainsFinish;
+ }
+
+ DomainTrustInfoLength = EntriesRead * sizeof(LSA_TRUST_INFORMATION);
+
+ //
+ // Now construct the information to be returned to the caller. We
+ // first need to allocate an array of structures of type
+ // LSA_TRUST_INFORMATION each entry of which will be filled in with
+ // the Sid of the domain and its Unicode Name.
+ //
+
+ DomainTrustInfo = MIDL_user_allocate( DomainTrustInfoLength );
+
+ //
+ // Initialize all pointers to Sids and Unicode buffers in the
+ // DomainTrustInfo array to zero. The error path of this routine
+ // assumes that a non-zero value of a Sid or Unicode buffer indicates
+ // that memory is to be freed.
+ //
+
+ RtlZeroMemory( DomainTrustInfo, DomainTrustInfoLength );
+
+ for (Sids = DbEnumerationBuffer.Sids, NextDomainTrustInfo = DomainTrustInfo;
+ NextDomainTrustInfo < DomainTrustInfo + EntriesRead;
+ Sids++, NextDomainTrustInfo++
+ ) {
+
+ NextDomainTrustInfo->Sid = *Sids;
+
+ //
+ // Open the Trusted Domain object. This call is trusted, i.e.
+ // no access validation or impersonation is required. Also,
+ // the Lsa Database is already locked so we do not need to
+ // lock it again.
+ //
+
+ Status = LsapDbOpenTrustedDomain(
+ PolicyHandle,
+ NextDomainTrustInfo->Sid,
+ (ACCESS_MASK) 0,
+ &TrustedDomainHandle,
+ LSAP_DB_TRUSTED
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Read the Domain Name
+ //
+
+ LsapDbInitializeAttribute(
+ &DomainNameAttribute,
+ &LsapDbNames[TrDmName],
+ NULL,
+ 0L,
+ FALSE
+ );
+
+ Status = LsapDbReadAttribute(TrustedDomainHandle, &DomainNameAttribute);
+
+ //
+ // Before checking status, close this Trusted Domain Object.
+ //
+
+ SecondaryStatus = LsapDbCloseObject(
+ &TrustedDomainHandle,
+ LSAP_DB_DEREFERENCE_CONTR
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+#if DBG
+ DbgPrint( "LsarEnumerateTrustedDomains - Reading Domain Name\n" );
+
+ DbgPrint( " failed. Error 0x%lx reading Trusted Domain Name attribute\n",
+ Status);
+#endif //DBG
+
+ break;
+ }
+
+ //
+ // Copy the Unicode Name field to the output. Original buffer will
+ // be freed in Finish section.
+ //
+
+ Status = LsapDbCopyUnicodeAttribute(
+ &NextDomainTrustInfo->Name,
+ &DomainNameAttribute,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowEnumerateTrustedDomainsError;
+ }
+
+SlowEnumerateTrustedDomainsFinish:
+
+ //
+ // Fill in returned Enumeration Structure, returning 0 or NULL for
+ // fields in the error case.
+ //
+
+ EnumerationBuffer->Information = (PLSAPR_TRUST_INFORMATION) DomainTrustInfo;
+ EnumerationBuffer->EntriesRead = DbEnumerationBuffer.EntriesRead;
+
+ //
+ // If necessary, free the Domain Name Attribute Value buffer which
+ // holds a self relative Unicode String.
+ //
+
+ if (DomainNameAttribute.AttributeValue != NULL) {
+
+ MIDL_user_free( DomainNameAttribute.AttributeValue );
+ DomainNameAttribute.AttributeValue = NULL;
+ }
+
+ //
+ // If necessary, dereference the Policy Object, release the LSA Database
+ // lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+SlowEnumerateTrustedDomainsError:
+
+ //
+ // If necessary, free the Unicode buffers allocated by
+ // LsapDbCopyUnicodeAttribute
+ //
+
+ if (DomainTrustInfo != NULL) {
+
+ for (NextDomainTrustInfo = DomainTrustInfo;
+ NextDomainTrustInfo < DomainTrustInfo + EntriesRead;
+ NextDomainTrustInfo++) {
+
+ if (NextDomainTrustInfo->Name.Buffer != NULL) {
+
+ MIDL_user_free( NextDomainTrustInfo->Name.Buffer );
+ NextDomainTrustInfo->Name.Buffer = NULL;
+ }
+ }
+ }
+
+ goto SlowEnumerateTrustedDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbVerifyInfoQueryTrustedDomain(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN BOOLEAN Trusted,
+ OUT PACCESS_MASK RequiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function validates a TrustedDomain Information Class. If valid, a mask
+ of the accesses required to set the TrustedDomain Information of the class is
+ returned.
+
+Arguments:
+
+ InformationClass - Specifies a TrustedDomain Information Class.
+
+ Trusted - TRUE if client is trusted, else FALSE. A trusted client
+ is allowed to query TrustedDomain for all Information Classes, whereas
+ a non-trusted client is restricted.
+
+ RequiredAccess - Points to variable that will receive a mask of the
+ accesses required to query the given class of TrustedDomain Information.
+ If an error is returned, this value is cleared to 0.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The TrustedDomain Information Class provided is
+ valid and the information provided is consistent with this
+ class.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter:
+
+ Information Class is invalid
+ TrustedDomain Information not valid for the class
+--*/
+
+{
+ if (LsapDbValidInfoTrustedDomain( InformationClass, NULL)) {
+
+ *RequiredAccess = LsapDbRequiredAccessQueryTrustedDomain[InformationClass];
+ return(STATUS_SUCCESS);
+ }
+
+ return(STATUS_INVALID_PARAMETER);
+
+ //
+ // Currently, all TrustedDomain information classes may be queried
+ // by non-trusted callers, so the Trusted parameter is not accessed.
+ //
+
+ UNREFERENCED_PARAMETER(Trusted);
+}
+
+
+NTSTATUS
+LsapDbVerifyInfoSetTrustedDomain(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation,
+ IN BOOLEAN Trusted,
+ OUT PACCESS_MASK RequiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function validates a TrustedDomain Information Class and verifies
+ that the provided TrustedDomain Information is valid for the class.
+ If valid, a mask of the accesses required to set the TrustedDomain
+ Information of the class is returned.
+
+Arguments:
+
+ InformationClass - Specifies a TrustedDomain Information Class.
+
+ TrustedDomainInformation - Points to TrustedDomain Information to be set.
+
+ Trusted - TRUE if client is trusted, else FALSE. A trusted client
+ is allowed to set TrustedDomain for all Information Classes, whereas
+ a non-trusted client is restricted.
+
+ RequiredAccess - Points to variable that will receive a mask of the
+ accesses required to set the given class of TrustedDomain Information.
+ If an error is returned, this value is cleared to 0.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The TrustedDomain Information Class provided is
+ valid and the information provided is consistent with this
+ class.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter:
+
+ Information Class is invalid
+ Information Class is invalid for non-trusted clients
+ TrustedDomain Information not valid for the class
+--*/
+
+{
+ //
+ // Verify that the information class is valid and that the TrustedDomain
+ // Information provided is valid for the class.
+ //
+
+ if (LsapDbValidInfoTrustedDomain( InformationClass, TrustedDomainInformation)) {
+
+ //
+ // Non-trusted callers are not allowed to set the
+ // TrustedAccountNameInformation information class.
+ //
+
+ if (!Trusted) {
+
+ if (InformationClass == TrustedDomainNameInformation) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ *RequiredAccess = LsapDbRequiredAccessSetTrustedDomain[InformationClass];
+ return(STATUS_SUCCESS);
+ }
+
+ return(STATUS_INVALID_PARAMETER);
+}
+
+
+BOOLEAN
+LsapDbValidInfoTrustedDomain(
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN OPTIONAL PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function validates a TrustedDomain Information Class and optionally verifies
+ that provided TrustedDomain Information is valid for the class.
+
+Arguments:
+
+ InformationClass - Specifies a TrustedDomain Information Class.
+
+ TrustedDomainInformation - Optionally points to TrustedDomain Information. If
+ NULL is specified, no TrustedDomain Information checking takes place.
+
+Return Values:
+
+ BOOLEAN - TRUE if the TrustedDomain information class provided is
+ valid, else FALSE.
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+
+ PTRUSTED_DOMAIN_NAME_INFO TrustedDomainNameInfo;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo;
+ PTRUSTED_POSIX_OFFSET_INFO TrustedPosixOffsetInfo;
+
+ //
+ // Validate the Information Class
+ //
+
+ if ((InformationClass >= TrustedDomainNameInformation) &&
+ (InformationClass <= TrustedPosixOffsetInformation)) {
+
+ if (TrustedDomainInformation == NULL) {
+
+ return(TRUE);
+ }
+
+ switch (InformationClass) {
+
+ case TrustedDomainNameInformation:
+
+ TrustedDomainNameInfo = (PTRUSTED_DOMAIN_NAME_INFO) TrustedDomainInformation;
+
+ break;
+
+ case TrustedControllersInformation:
+
+ TrustedControllersInfo = (PTRUSTED_CONTROLLERS_INFO) TrustedDomainInformation;
+
+ break;
+
+ case TrustedPosixOffsetInformation:
+
+ TrustedPosixOffsetInfo = (PTRUSTED_POSIX_OFFSET_INFO) TrustedDomainInformation;
+
+ break;
+
+ default:
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+
+ return(BooleanStatus);
+}
+
+
+NTSTATUS
+LsapDbLookupSidTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_SID DomainSid,
+ OUT PLSAPR_TRUST_INFORMATION *TrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up a given Trusted Domain Sid in the Trusted
+ Domain List and returns Trust Information consisting of its
+ Sid and Name.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+ DomainSid - Pointer to a Sid that will be compared with the list of
+ Sids of Trusted Domains.
+
+ TrustInformation - Receives the a pointer to the Trust Information
+ (Sid and Name) of the Trusted Domain specified by DomainSid
+ within the Trusted Domain List.
+
+ NOTE: This routine assumes that the Trusted Domain List
+ will not be updated while any Lookup operations are pending.
+ Thus, the pointer returned for TrustInformation will remain
+ valid.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The domain was found.
+
+ STATUS_NO_SUCH_DOMAIN - The domain was not found.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG SectionIndex;
+ LSAPR_TRUST_INFORMATION InputTrustInformation;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
+
+ InputTrustInformation.Sid = DomainSid;
+ InputTrustInformation.Name.Buffer = NULL;
+ InputTrustInformation.Name.Length = 0;
+ InputTrustInformation.Name.MaximumLength = 0;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ Status = LsapDbLookupEntryTrustedDomainList(
+ TrustedDomainList,
+ &InputTrustInformation,
+ &TrustedDomainListSection,
+ &SectionIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidTrustedDomainListError;
+ }
+
+ //
+ // Return pointer to Trust Information
+ //
+
+ *TrustInformation = &TrustedDomainListSection->Domains[SectionIndex];
+
+LookupSidTrustedDomainListFinish:
+
+ return(Status);
+
+LookupSidTrustedDomainListError:
+
+ *TrustInformation = NULL;
+ goto LookupSidTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupNameTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_UNICODE_STRING DomainName,
+ OUT PLSAPR_TRUST_INFORMATION *TrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up a given Trusted Domain Name in the Trusted
+ Domain List and returns Trust Information consisting of its
+ Sid and Name.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+ DomainName - Pointer to a Unicode Name that will be compared with the
+ list of Names of Trusted Domains.
+
+ TrustInformation - Receives the a pointer to the Trust Information
+ (Sid and Name) of the Trusted Domain described by DomainName
+ within the Trusted Domain List.
+
+ NOTE: This routine assumes that the Trusted Domain List
+ will not be updated while any Lookup operations are pending.
+ Thus, the pointer returned for TrustInformation will remain
+ valid.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The domain was found.
+
+ STATUS_NO_SUCH_DOMAIN - The domain was not found.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG SectionIndex;
+ LSAPR_TRUST_INFORMATION InputTrustInformation;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
+
+ InputTrustInformation.Sid = NULL;
+ InputTrustInformation.Name = *DomainName;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ Status = LsapDbLookupEntryTrustedDomainList(
+ TrustedDomainList,
+ &InputTrustInformation,
+ &TrustedDomainListSection,
+ &SectionIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNameTrustedDomainListError;
+ }
+
+ //
+ // Return pointer to Trust Information
+ //
+
+ *TrustInformation = &TrustedDomainListSection->Domains[SectionIndex];
+
+LookupNameTrustedDomainListFinish:
+
+ return(Status);
+
+LookupNameTrustedDomainListError:
+
+ *TrustInformation = NULL;
+ goto LookupNameTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupEntryTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION *TrustedDomainListSection,
+ OUT PULONG SectionIndex
+ )
+
+/*++
+
+Routine Decsription:
+
+ This function locates an entry for a Trusted Domain in the Trusted
+ Domain List, given Trust Information containing either a Domain Sid
+ or a Domain Name.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+ TrustInformation - Points to the Sid and Name of a Trusted Domain.
+
+ TrustedDomainListSection - Receives pointer to the section of the
+ Trusted Domain List containing the Trust Information for the
+ specified Trusted Domain.
+
+ SectionIndex - Receives the index of the entry for the specified
+ Trusted Domain within the Trusted Domain List Section returned
+ via TrustedDomainListSection.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The domain was found.
+
+ STATUS_NO_SUCH_DOMAIN - The domain was not found.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION NextListSection = NULL;
+
+ ULONG ScanSectionIndex;
+ BOOLEAN DomainFound = FALSE;
+ BOOLEAN LookupSid = TRUE;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION AnchorListSection = NULL;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ //
+ // Decide if we're to lookup a Domain Sid or a Domain Name.
+ //
+
+ if (TrustInformation->Sid == NULL) {
+
+ LookupSid = FALSE;
+ }
+
+ //
+ // Scan the Trusted Domain List looking for a match on Sid or Name
+ //
+
+ AnchorListSection = &TrustedDomainList->DummyAnchorListSection;
+ NextListSection = (PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ AnchorListSection->Links.Flink;
+
+ while (NextListSection != AnchorListSection) {
+
+ for (ScanSectionIndex = 0;
+ ScanSectionIndex < NextListSection->UsedCount;
+ ScanSectionIndex++
+ ) {
+
+ //
+ // Break out if we find a match.
+ //
+
+ if (LookupSid) {
+
+ if (RtlEqualSid(
+ (PSID) TrustInformation->Sid,
+ (PSID) NextListSection->Domains[ScanSectionIndex].Sid
+ )) {
+
+ DomainFound = TRUE;
+ break;
+ }
+
+ } else {
+
+ if (RtlEqualDomainName(
+ (PUNICODE_STRING) &TrustInformation->Name,
+ (PUNICODE_STRING) &NextListSection->Domains[ScanSectionIndex].Name
+ )) {
+
+ DomainFound = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (DomainFound) {
+
+ break;
+ }
+
+ NextListSection = (PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ NextListSection->Links.Flink;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupEntryTrustedDomainListError;
+ }
+
+ Status = STATUS_NO_SUCH_DOMAIN;
+
+ if (!DomainFound) {
+
+ goto LookupEntryTrustedDomainListError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ *TrustedDomainListSection = NextListSection;
+ *SectionIndex = ScanSectionIndex;
+
+LookupEntryTrustedDomainListFinish:
+
+ return(Status);
+
+LookupEntryTrustedDomainListError:
+
+ goto LookupEntryTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbInsertTrustedDomainList(
+ IN ULONG Count,
+ IN PLSAPR_TRUST_INFORMATION Domains
+ )
+
+/*++
+
+Routine Description:
+
+ This function inserts a Trusted Domain in the Trusted Domain List.
+ It is called when a Trusted Domain object is created in the Lsa
+ Policy Database. The List will not be altered while it is active.
+
+Arguments:
+
+ Count - Specifies count of Trusted Domains to be added to the
+ Trusted Domain List. This value should match the
+ number of elements in the Domains array.
+
+ Domains - Points to an array of LSAPR_TRUST_INFORMATION
+ structures each containing the Name and Sid of a Trusted
+ Domain.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
+ PLSAPR_TRUST_INFORMATION TrustedDomains = NULL;
+ BOOLEAN AcquiredListWriteLock = FALSE;
+ LSAP_MM_FREE_LIST FreeList;
+ ULONG DomainIndex;
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+
+ //
+ // If this Trusted Domain List is not marked
+ // as a valid cache, do nothing.
+ //
+
+ if (!LsapDbIsCacheValid(TrustedDomainObject)) {
+
+ goto InsertTrustedDomainListFinish;
+ }
+
+ //
+ // Create a Free List.
+ //
+
+ Status = LsapMmCreateFreeList( &FreeList, (ULONG) 2 * Count );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Acquire exclusive write lock for the Trusted Domain List.
+ //
+
+ Status = LsapDbAcquireWriteLockTrustedDomainList( NULL );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InsertTrustedDomainListError;
+ }
+
+ AcquiredListWriteLock = TRUE;
+
+ //
+ // If the Trusted Domain List is not valid, quit and do nothing.
+ //
+
+ if (LsapDbIsValidTrustedDomainList( TrustedDomainList )) {
+
+
+ //
+ // The Trusted Domain List is referenced by us, but otherwise inactive
+ // so we can update it. Create a new Trusted Domain List section for
+ // all of the Trusted Domains to be added to the list.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ TrustedDomainListSection = MIDL_user_allocate(
+ sizeof(LSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ );
+
+ if (TrustedDomainListSection == NULL) {
+
+ goto InsertTrustedDomainListError;
+ }
+
+ //
+ // Allocate memory for the List of Trust Information entries.
+ //
+
+ TrustedDomains = MIDL_user_allocate( Count * sizeof(LSAPR_TRUST_INFORMATION) );
+
+ if (TrustedDomains == NULL) {
+
+ goto InsertTrustedDomainListError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Allocate memory for and copy the input array of Domain Trust
+ // Information entries.
+ //
+
+ RtlCopyMemory( TrustedDomains, Domains, Count * sizeof(LSAPR_TRUST_INFORMATION));
+
+ for (DomainIndex = 0; DomainIndex < Count; DomainIndex++) {
+
+ Status = LsapRpcCopyUnicodeString(
+ &FreeList,
+ (PUNICODE_STRING) &TrustedDomains[DomainIndex].Name,
+ (PUNICODE_STRING) &Domains[DomainIndex].Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ Status = LsapRpcCopySid(
+ &FreeList,
+ (PSID) &TrustedDomains[DomainIndex].Sid,
+ (PSID) Domains[DomainIndex].Sid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InsertTrustedDomainListError;
+ }
+
+ TrustedDomainListSection->UsedCount = Count;
+ TrustedDomainListSection->MaximumCount = Count;
+ TrustedDomainListSection->Domains = TrustedDomains;
+
+ //
+ // Insert the new Trusted Domain List section into the Trusted Domain
+ // List at the end.
+ //
+
+ TrustedDomainListSection->Links.Flink =
+ (PLIST_ENTRY) TrustedDomainList->AnchorListSection;
+ TrustedDomainListSection->Links.Blink =
+ TrustedDomainList->AnchorListSection->Links.Blink;
+
+ TrustedDomainList->AnchorListSection->Links.Blink->Flink =
+ (PLIST_ENTRY) TrustedDomainListSection;
+
+ TrustedDomainList->AnchorListSection->Links.Blink =
+ (PLIST_ENTRY) TrustedDomainListSection;
+
+ }
+
+ //
+ // Delete the Free List structure, leaving the buffer pointers intact.
+ //
+
+ LsapMmCleanupFreeList( &FreeList, 0 );
+ }
+
+InsertTrustedDomainListFinish:
+
+ //
+ // If necessary, release the Trusted Domain List Write Lock.
+ //
+
+ if (AcquiredListWriteLock) {
+
+ LsapDbReleaseWriteLockTrustedDomainList( NULL );
+ AcquiredListWriteLock = FALSE;
+ }
+
+ return(Status);
+
+InsertTrustedDomainListError:
+
+ //
+ // If necessary, free the memory allocated for the Trusted Domain List
+ // section and the array of Trust Information entries within it.
+ //
+
+ if (TrustedDomainListSection != NULL) {
+
+ //
+ // Free the Trust Infromation entries.
+ //
+
+ LsapMmCleanupFreeList( &FreeList, LSAP_MM_FREE_BUFFERS );
+
+ if (TrustedDomainListSection->Domains != NULL) {
+
+ MIDL_user_free( TrustedDomainListSection->Domains );
+ }
+
+ MIDL_user_free( TrustedDomainListSection );
+ TrustedDomainListSection = NULL;
+ }
+
+ goto InsertTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbDeleteTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function deletes a Trusted Domain from the Trusted Domain List
+ if that list is marked as valid. The Trusted Domain List will not
+ be altered while there are Lookup operations pending.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+ TrustInformation - Points to the Sid and Name of a Trusted Domain.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
+ ULONG SectionIndex;
+ ULONG TrailingMoveCount;
+ PLSAPR_TRUST_INFORMATION SourceEntry, TargetEntry;
+ BOOLEAN AcquiredListWriteLock = FALSE;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ //
+ // If this Trusted Domain List is the local list and it is not marked
+ // as a valid cache, do nothing.
+ //
+
+ if ((TrustedDomainList == NULL) || (TrustedDomainList == &LsapDbTrustedDomainList)) {
+
+ if (!LsapDbIsCacheValid(TrustedDomainObject)) {
+
+ goto DeleteTrustedDomainListFinish;
+ }
+ }
+
+ //
+ // Acquire exclusive write lock for the Trusted Domain List.
+ //
+
+ Status = LsapDbAcquireWriteLockTrustedDomainList( TrustedDomainList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteTrustedDomainListError;
+ }
+
+ AcquiredListWriteLock = TRUE;
+
+ //
+ // If the Trusted Domain List is not valid, quit and do nothing.
+ //
+
+ if (!LsapDbIsValidTrustedDomainList) {
+
+ goto DeleteTrustedDomainListFinish;
+ }
+
+ //
+ // The Trusted Domain List is referenced by us, but otherwise inactive.
+ // Update the List. First, we need to locate the entry to be deleted.
+ //
+
+ Status = LsapDbLookupEntryTrustedDomainList(
+ TrustedDomainList,
+ TrustInformation,
+ &TrustedDomainListSection,
+ &SectionIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteTrustedDomainListError;
+ }
+
+ TargetEntry = &(TrustedDomainListSection->Domains[SectionIndex]);
+
+ //
+ // Free up the Sid and Name in the deleted Trust Information Entry.
+ //
+
+ RtlFreeUnicodeString( (PUNICODE_STRING) &TargetEntry->Name );
+ MIDL_user_free ( TargetEntry->Sid );
+
+ //
+ // Now compress the trailing entries in the section
+ //
+
+ TrustedDomainListSection->UsedCount--;
+
+ if (TrustedDomainListSection->UsedCount > 0) {
+
+ TrailingMoveCount = TrustedDomainListSection->UsedCount - SectionIndex;
+
+ if (TrailingMoveCount > 0) {
+
+ SourceEntry = (TargetEntry + 1);
+
+ // Warning! This is an overlapping move but from higher to lower
+ // addresses, so RtlCopyMemory is OK.
+
+ RtlCopyMemory(
+ TargetEntry,
+ SourceEntry,
+ TrailingMoveCount * sizeof (LSA_TRUST_INFORMATION)
+ );
+ }
+
+ } else {
+
+ //
+ // Used Count is now zero. Unlink the list section.
+ //
+
+ TrustedDomainListSection->Links.Flink->Blink =
+ TrustedDomainListSection->Links.Blink;
+
+ TrustedDomainListSection->Links.Blink->Flink =
+ TrustedDomainListSection->Links.Flink;
+
+ //
+ // Free the List section's memory
+ //
+
+ if (TrustedDomainListSection->Domains != NULL) {
+
+ MIDL_user_free( TrustedDomainListSection->Domains );
+ TrustedDomainListSection->Domains = NULL;
+ }
+
+ MIDL_user_free( TrustedDomainListSection );
+ TrustedDomainListSection = NULL;
+ }
+
+DeleteTrustedDomainListFinish:
+
+ //
+ // If necessary, release the Trusted Domain List Write Lock.
+ //
+
+ if (AcquiredListWriteLock) {
+
+ LsapDbReleaseWriteLockTrustedDomainList( NULL );
+ AcquiredListWriteLock = FALSE;
+ }
+
+ return(Status);
+
+DeleteTrustedDomainListError:
+
+ goto DeleteTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbTraverseTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION *TrustedDomainListSection,
+ IN OUT PULONG SectionIndex,
+ OUT OPTIONAL PLSAPR_TRUST_INFORMATION *TrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to traverse the Trusted Domain List. Each call
+ yields a pointer to the Trust Information for the next Trusted Domain
+ on the list.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+ TrustedDomainListSection - A pointer to the relevant section on the
+ Trusted Domain List is maintained in this location. Prior to the
+ first call to the routine, this location must be initialized to
+ NULL.
+
+ SectionIndex - An index to the relevant entry within the relevant
+ section is maintained in this location.
+
+ TrustInformation - If specified, receives a pointer to the Trust
+ Information for the next Trusted Domain, or NULL if there are no more.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - This is returned when the final entry is being
+ returned.
+
+ STATUS_MORE_ENTRIES - There are more entries in the list, so call
+ again.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries after the
+ one returned.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION OutputTrustedDomainListSection = *TrustedDomainListSection;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION NextTrustedDomainListSection = NULL;
+ ULONG OutputSectionIndex = *SectionIndex;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ //
+ // If there is a present section selected, examine it.
+ //
+
+ if (OutputTrustedDomainListSection != NULL) {
+
+ OutputSectionIndex++;
+
+ //
+ // If we've used up all the entries in this section, see if there is
+ // another section.
+ //
+
+ if (OutputSectionIndex >= OutputTrustedDomainListSection->UsedCount) {
+
+ OutputTrustedDomainListSection =
+ (PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ OutputTrustedDomainListSection->Links.Flink;
+
+ Status = STATUS_NO_MORE_ENTRIES;
+
+ if (OutputTrustedDomainListSection == TrustedDomainList->AnchorListSection) {
+
+ goto TraverseTrustedDomainListError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // There is another section. Return Section Index 0. Note that
+ // all sections have at least one entry.
+ //
+
+ OutputSectionIndex = (ULONG) 0;
+ }
+
+ } else {
+
+ //
+ // The NULL section was specified, reset pointer to the first section
+ // in the list.
+ //
+
+ OutputTrustedDomainListSection =
+ (PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ TrustedDomainList->AnchorListSection->Links.Flink;
+
+ OutputSectionIndex = (ULONG) 0;
+
+ Status = STATUS_NO_MORE_ENTRIES;
+
+ if (OutputTrustedDomainListSection == TrustedDomainList->AnchorListSection) {
+
+ goto TraverseTrustedDomainListError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // We have successfully selected the next entry. Adjust the success
+ // status to indicate if there are more entries beyond the one being
+ // returned.
+ //
+
+ if ((OutputSectionIndex + (ULONG) 1) < OutputTrustedDomainListSection->UsedCount) {
+
+ Status = STATUS_MORE_ENTRIES;
+
+ } else {
+
+ NextTrustedDomainListSection =
+ (PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ OutputTrustedDomainListSection->Links.Flink;
+
+ if (NextTrustedDomainListSection != TrustedDomainList->AnchorListSection) {
+
+ Status = STATUS_MORE_ENTRIES;
+ }
+ }
+
+TraverseTrustedDomainListFinish:
+
+ *TrustedDomainListSection = OutputTrustedDomainListSection;
+ *SectionIndex = OutputSectionIndex;
+
+ if ((OutputTrustedDomainListSection != NULL) && TrustInformation != NULL) {
+
+ *TrustInformation = &OutputTrustedDomainListSection->Domains[ OutputSectionIndex ];
+ }
+
+ return(Status);
+
+TraverseTrustedDomainListError:
+
+ OutputTrustedDomainListSection = NULL;
+ OutputSectionIndex = (ULONG) 0;
+ goto TraverseTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbEnumerateTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function enumerates zero or more Trusted Domains on the
+ Trusted Domain List. Since there may be more information than can be
+ returned in a single call of the routine, multiple calls can be made to
+ get all of the information. To support this feature, the caller is
+ provided with a handle that can be used across calls to the API. On the
+ initial call, EnumerationContext should point to a variable that has
+ been initialized to 0.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationBuffer - Pointer to an enumeration structure that will receive
+ a count of the Trusted Domains enumerated on this call and a pointer to
+ an array of entries containing information for each enumerated
+ Trusted Domain.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_MORE_ENTRIES - The call completed successfully. There
+ are more entries so call again. This is a success status.
+
+ STATUS_NO_MORE_ENTRIES - No entries have been returned because there
+ are no more entries in the list.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS, EnumerationStatus = STATUS_SUCCESS;
+ ULONG LengthEnumeratedInfo = (ULONG) 0;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION StartingTrustedDomainListSection = NULL;
+ ULONG SectionIndex = (ULONG) 0;
+ ULONG StartingSectionIndex = (ULONG) 0;
+ PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
+ PLSAPR_TRUST_INFORMATION StartingTrustInformation = NULL;
+ PLSAPR_TRUST_INFORMATION DomainTrustInfo = NULL;
+ ULONG EntryNumber, EntriesRead, DomainTrustInfoLength;
+ ULONG InitialEnumerationStatus = STATUS_SUCCESS;
+ BOOLEAN AcquiredTrustedDomainListReadLock = FALSE;
+
+ EntriesRead = (ULONG) 0;
+
+ //
+ // Acquire the Read Lock for the Trusted Domain List
+ //
+
+ Status = LsapDbAcquireReadLockTrustedDomainList( TrustedDomainList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto EnumerateTrustedDomainListError;
+ }
+
+ AcquiredTrustedDomainListReadLock = TRUE;
+
+ //
+ // Verify that the Trusted Domain List is marked as valid.
+ //
+
+
+ if (!LsapDbIsValidTrustedDomainList( TrustedDomainList )) {
+
+ Status = STATUS_INVALID_SERVER_STATE;
+ goto EnumerateTrustedDomainListError;
+ }
+
+ //
+ // Find the starting point using the Enumeration Context Variable.
+ // This variable specifies an unsigned integer, which is the
+ // number of the entry in the list at which to begin the enumeration.
+ //
+
+ Status = LsapDbLocateEntryNumberTrustedDomainList(
+ TrustedDomainList,
+ *EnumerationContext,
+ &StartingTrustedDomainListSection,
+ &StartingSectionIndex,
+ &StartingTrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto EnumerateTrustedDomainListError;
+ }
+
+ InitialEnumerationStatus = Status;
+
+ //
+ // Now scan the Trusted Domain List to calculate how many
+ // entries we can return and the length of the buffer required.
+ // We use the PreferedMaximumLength value as a guide by accumulating
+ // the actual length of Trust Information structures and their
+ // contents until we either reach the end of the Trusted Domain List
+ // or until we first exceed the PreferedMaximumLength value. Thus,
+ // the amount of information returned typically exceeds the
+ // PreferedmaximumLength value by a smail amount, namely the
+ // size of the Trust Information for a single domain.
+ //
+
+ TrustedDomainListSection = StartingTrustedDomainListSection;
+ SectionIndex = StartingSectionIndex;
+ TrustInformation = StartingTrustInformation;
+
+ EntryNumber = (ULONG) 0;
+
+ EnumerationStatus = InitialEnumerationStatus;
+
+ do {
+
+ //
+ // Add in the length of the data to be returned for this
+ // Domain's Trust Information. We count the length of the
+ // Trust Information structure plus the length of the unicode
+ // Domain Name and Sid within it.
+ //
+
+ LengthEnumeratedInfo += sizeof(LSA_TRUST_INFORMATION) +
+ RtlLengthSid( (PSID) TrustInformation->Sid ) +
+ TrustInformation->Name.MaximumLength;
+
+ //
+ // If there are no more entries to enumerate, quit.
+ //
+
+ if (EnumerationStatus != STATUS_MORE_ENTRIES) {
+
+ break;
+ }
+
+ //
+ // Point at the next entry in the Trusted Domain List
+ //
+
+ Status = LsapDbTraverseTrustedDomainList(
+ TrustedDomainList,
+ &TrustedDomainListSection,
+ &SectionIndex,
+ &TrustInformation
+ );
+
+ EnumerationStatus = Status;
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ EntryNumber++;
+
+ } while (LengthEnumeratedInfo < PreferedMaximumLength);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto EnumerateTrustedDomainListError;
+ }
+
+ //
+ // We have successfully processed one or more entries.
+ //
+
+ EntriesRead = EntryNumber + (ULONG) 1;
+
+ //
+ // Allocate memory for the array of TrustInformation entries to be
+ // returned.
+ //
+
+ DomainTrustInfoLength = EntriesRead * sizeof(LSA_TRUST_INFORMATION);
+
+ //
+ // Now construct the information to be returned to the caller. We
+ // first need to allocate an array of structures of type
+ // LSA_TRUST_INFORMATION each entry of which will be filled in with
+ // the Sid of the domain and its Unicode Name.
+ //
+
+ DomainTrustInfo = MIDL_user_allocate( DomainTrustInfoLength );
+
+ if (DomainTrustInfo == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto EnumerateTrustedDomainListError;
+ }
+
+ RtlZeroMemory ( DomainTrustInfo, DomainTrustInfoLength );
+
+ //
+ // Now read through the Trusted Domains again to copy the output
+ // information.
+ //
+
+ TrustedDomainListSection = StartingTrustedDomainListSection;
+ SectionIndex = StartingSectionIndex;
+ TrustInformation = StartingTrustInformation;
+
+ Status = InitialEnumerationStatus;
+ EntryNumber = (ULONG) 0;
+
+ do {
+
+ //
+ // Save away the enumeration status
+ //
+
+ EnumerationStatus = Status;
+
+ //
+ // Copy in the Trust Information.
+ //
+
+ Status = LsapRpcCopyTrustInformation(
+ NULL,
+ &DomainTrustInfo[ EntryNumber ],
+ TrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // If there are no more entries to enumerate, quit.
+ //
+
+ if (EnumerationStatus != STATUS_MORE_ENTRIES) {
+
+ break;
+ }
+
+ //
+ // Point at the next entry in the Trusted Domain List
+ //
+
+ Status = LsapDbTraverseTrustedDomainList(
+ TrustedDomainList,
+ &TrustedDomainListSection,
+ &SectionIndex,
+ &TrustInformation
+ );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ EntryNumber++;
+
+ } while (EntryNumber < EntriesRead);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto EnumerateTrustedDomainListError;
+ }
+
+ (*EnumerationContext) += EntriesRead;
+
+EnumerateTrustedDomainListFinish:
+
+ //
+ // If necessary, release the Trusted Domain List Read Lock.
+ //
+
+ if (AcquiredTrustedDomainListReadLock) {
+
+ LsapDbReleaseReadLockTrustedDomainList( TrustedDomainList );
+ AcquiredTrustedDomainListReadLock = FALSE;
+ }
+
+ //
+ // Fill in returned Enumeration Structure, returning 0 or NULL for
+ // fields in the error case.
+ //
+
+ EnumerationBuffer->Information = (PLSAPR_TRUST_INFORMATION) DomainTrustInfo;
+ EnumerationBuffer->EntriesRead = EntriesRead;
+
+ //
+ // If a successful status is being returned, return the preserved
+ // Enumeration Status. This status is set to STATUS_MORE_ENTRIES
+ // if there are more entries in the list.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = EnumerationStatus;
+ }
+
+ return(Status);
+
+EnumerateTrustedDomainListError:
+
+ //
+ // If necessary, free the DomainTrustInfo array and all of its entries.
+ //
+
+ if (DomainTrustInfo != NULL) {
+
+ LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER ( EnumerationBuffer );
+ MIDL_user_free( DomainTrustInfo );
+ DomainTrustInfo = NULL;
+ EntriesRead = (ULONG) 0;
+ }
+
+ goto EnumerateTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbLocateEntryNumberTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN ULONG EntryNumber,
+ OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION *TrustedDomainListSection,
+ OUT PULONG SectionIndex,
+ OUT OPTIONAL PLSAPR_TRUST_INFORMATION *TrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ Given an Entry Number n, this function obtains the pointer to the nth
+ entry (if any) in a Trusted Domain List. The first entry in the
+ list is entry number 0.
+
+ WARNING: The caller of this function must hold a lock for the
+ Trusted Domain List. The valditiy of the returned pointers
+ is guaranteed only while that lock is held.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+ EntryNumber - Specifies the entry number (zero for first entry)
+ to be referenced.
+
+ TrustedDomainListSection - Receives a pointer to the Trusted
+ Domain List Section containing the entry corresponding to the
+ given EntryNumber. If no such entry exists, NULL is returned.
+
+ SectionIndex - Receives the index value of the entry corresponding
+ to the given EntryNumber. If no such entry exists, NULL is
+ returned.
+
+ TrustInformation - If non NULL, receives a pointer to the Trust
+ Information for the entry being returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - Call completed successfully and there are no
+ entries beyond the entry returned.
+
+ STATUS_MORE_ENTRIES - Call completed successfully and there are
+ more entries beyond the entry returned.
+
+ STATUS_NO_MORE_ENTRIES - There is no entry with the specified
+ EntryNumber
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG CurrentNumber = (ULONG) 0;
+ PLSAPR_TRUST_INFORMATION OutputTrustInformation = NULL;
+
+ for (CurrentNumber = 0; CurrentNumber <= EntryNumber; CurrentNumber++) {
+
+ Status = LsapDbTraverseTrustedDomainList(
+ TrustedDomainList,
+ TrustedDomainListSection,
+ SectionIndex,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LocateEntryNumberTrustedDomainListError;
+ }
+
+ if (*TrustedDomainListSection != NULL) {
+
+ OutputTrustInformation = &((*TrustedDomainListSection)->Domains[ *SectionIndex ]);
+ }
+
+LocateEntryNumberTrustedDomainListFinish:
+
+ if (ARGUMENT_PRESENT( TrustInformation )) {
+
+ *TrustInformation = OutputTrustInformation;
+ }
+
+ return(Status);
+
+LocateEntryNumberTrustedDomainListError:
+
+ goto LocateEntryNumberTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbBuildTrustedDomainList(
+ IN OPTIONAL LSA_HANDLE PolicyHandle,
+ OUT OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes a Trusted Domain List by enumerating all
+ of the Trusted Domain objects in the specified target system's
+ Policy Database. For a Windows Nt system (Workstation) the list
+ contains only the Primary Domain. For a LanManNt system (DC), the
+ list contains zero or more Trusted Domain objects. Note that the
+ list contains only those domains for which Trusted Domain objects
+ exist in the local LSA Policy Database. If for example, a DC
+ trusted Domain A which in turn trusts Domain B, the list will not
+ contain an entry for Domain B unless there is a direct relationship.
+
+Arguments:
+
+ PolicyHandle - Handle to the LSA Policy Object in the target system's
+ Policy database. The handle must specify
+ POLICY_VIEW_LOCAL_INFORMATION access. If NULL is specified, the local
+ system is assumed.
+
+ TrustedDomainList - Pointer to Trusted Domain List structure to be initialized.
+ This parameter is optional only if initializing the local system
+ Trusted Domain List.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INVALID_PARAMETER - TrustedDomainList was NULL when
+ PolicyHandle was non-NULL.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS EnumerationStatus;
+ LSAPR_TRUSTED_ENUM_BUFFER TrustedDomains;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
+ ULONG EnumerationContext = 0;
+ BOOLEAN AcquiredListWriteLock = FALSE;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST OutputTrustedDomainList = TrustedDomainList;
+ PLSA_TRUST_INFORMATION RemoteTrustedDomains = NULL;
+ ULONG CountReturned;
+
+ //
+ // Verify input parameters
+ //
+
+ if (OutputTrustedDomainList == NULL) {
+
+ if (PolicyHandle == NULL) {
+
+ OutputTrustedDomainList = &LsapDbTrustedDomainList;
+
+ } else {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ goto BuildTrustedDomainListError;
+ }
+ }
+
+ //
+ // Initialize the Resource for the Trusted Domain List.
+ //
+
+ RtlInitializeResource( &OutputTrustedDomainList->Resource);
+
+ //
+ // Acquire exclusive write lock for the Trusted Domain List.
+ //
+
+ Status = LsapDbAcquireWriteLockTrustedDomainList( OutputTrustedDomainList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto BuildTrustedDomainListError;
+ }
+
+ AcquiredListWriteLock = TRUE;
+
+ //
+ // Initialize the Trusted Domain List to the empty state.
+ //
+
+ OutputTrustedDomainList->AnchorListSection = &OutputTrustedDomainList->DummyAnchorListSection;
+ OutputTrustedDomainList->AnchorListSection->Links.Flink =
+ OutputTrustedDomainList->AnchorListSection->Links.Blink =
+ (PLIST_ENTRY) &OutputTrustedDomainList->DummyAnchorListSection;
+
+ //
+ // Mark the Trusted Domain List as invalid
+ //
+
+ OutputTrustedDomainList->Valid = FALSE;
+
+ //
+ // Loop round, enumerating groups of Trusted Domain objects.
+ //
+ //
+ // Check for request to initialize the local system's Trusted Domain List.
+ //
+
+ if (PolicyHandle == NULL) {
+
+ do {
+
+ //
+ // Enumerate the next group of Trusted Domains
+ //
+
+ EnumerationStatus = Status = LsapDbSlowEnumerateTrustedDomains(
+ LsapPolicyHandle,
+ &EnumerationContext,
+ &TrustedDomains,
+ LSAP_DB_ENUM_DOMAIN_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // If the number of entries returned was zero, quit.
+ //
+
+ if (TrustedDomains.EntriesRead == (ULONG) 0) {
+
+ break;
+ }
+
+ //
+ // Link the array of Trust Information structures to the
+ // Trusted Domain List.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ TrustedDomainListSection = MIDL_user_allocate(sizeof(LSAP_DB_TRUSTED_DOMAIN_LIST_SECTION));
+
+ if (TrustedDomainListSection == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ TrustedDomainListSection->UsedCount = TrustedDomains.EntriesRead;
+ TrustedDomainListSection->MaximumCount = TrustedDomains.EntriesRead;
+ TrustedDomainListSection->Domains = TrustedDomains.Information;
+
+ TrustedDomainListSection->Links.Flink =
+ (PLIST_ENTRY) OutputTrustedDomainList->AnchorListSection;
+ TrustedDomainListSection->Links.Blink =
+ (PLIST_ENTRY) OutputTrustedDomainList->AnchorListSection->Links.Blink;
+
+ TrustedDomainListSection->Links.Flink->Blink =
+ (PLIST_ENTRY) TrustedDomainListSection;
+ TrustedDomainListSection->Links.Blink->Flink =
+ (PLIST_ENTRY) TrustedDomainListSection;
+
+ } while (EnumerationStatus != STATUS_NO_MORE_ENTRIES);
+
+ } else {
+
+ //
+ // Loop round, enumerating groups of Trusted Domain objects.
+ //
+
+ do {
+
+ //
+ // Enumerate the next group of Trusted Domains
+ //
+
+ CountReturned = (ULONG) 0;
+
+ EnumerationStatus = Status = LsaEnumerateTrustedDomains(
+ PolicyHandle,
+ &EnumerationContext,
+ (PVOID *) &RemoteTrustedDomains,
+ LSAP_DB_ENUM_DOMAIN_LENGTH,
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ break;
+ }
+ }
+
+ //
+ // Link the array of Trust Information structures to the
+ // Trusted Domain List.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ TrustedDomainListSection = MIDL_user_allocate(sizeof(LSAP_DB_TRUSTED_DOMAIN_LIST_SECTION));
+
+ if (TrustedDomainListSection == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ TrustedDomainListSection->UsedCount = CountReturned;
+ TrustedDomainListSection->MaximumCount = CountReturned;
+ TrustedDomainListSection->Domains = (PLSAPR_TRUST_INFORMATION) RemoteTrustedDomains;
+
+ TrustedDomainListSection->Links.Flink =
+ (PLIST_ENTRY) OutputTrustedDomainList->AnchorListSection;
+ TrustedDomainListSection->Links.Blink =
+ (PLIST_ENTRY) OutputTrustedDomainList->AnchorListSection->Links.Blink;
+
+ TrustedDomainListSection->Links.Flink->Blink =
+ (PLIST_ENTRY) TrustedDomainListSection;
+ TrustedDomainListSection->Links.Blink->Flink =
+ (PLIST_ENTRY) TrustedDomainListSection;
+
+ } while (EnumerationStatus != STATUS_NO_MORE_ENTRIES);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If STATUS_NO_MORE_ENTRIES was returned, there are no more
+ // trusted domains. Discard this status.
+ //
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ goto BuildTrustedDomainListError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Mark the Trusted Domain List as valid.
+ //
+
+ OutputTrustedDomainList->Valid = TRUE;
+
+BuildTrustedDomainListFinish:
+
+ //
+ // If necessary, release the Trusted Domain List Write Lock.
+ //
+
+ if (AcquiredListWriteLock) {
+
+ LsapDbReleaseWriteLockTrustedDomainList( OutputTrustedDomainList );
+ AcquiredListWriteLock = FALSE;
+ }
+
+ return(Status);
+
+BuildTrustedDomainListError:
+
+ goto BuildTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbDestroyTrustedDomainList(
+ IN PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the opposite of LsapDbBuildTrustedDomainList().
+
+Arguments:
+
+ TrustedDomainList - Pointer to Trusted Domain List structure
+ to be destroyed. This parameter must not be null.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+
+--*/
+
+{
+ NTSTATUS
+ Status = STATUS_SUCCESS;
+
+
+ BOOLEAN
+ DomainFound = FALSE,
+ LookupSid = TRUE;
+
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION
+ AnchorListSection = NULL,
+ NextListSection = NULL,
+ SectionToFree;
+
+
+ ASSERT(TrustedDomainList != NULL);
+ ASSERT(TrustedDomainList != &LsapDbTrustedDomainList);
+
+ //
+ // Acquire exclusive write lock for the Trusted Domain List.
+ //
+
+ Status = LsapDbAcquireWriteLockTrustedDomainList( TrustedDomainList );
+
+ if (NT_SUCCESS(Status)) {
+
+
+ //
+ // Free each entry in the trusted domain list
+ //
+
+ AnchorListSection = &TrustedDomainList->DummyAnchorListSection;
+ NextListSection = (PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ AnchorListSection->Links.Flink;
+
+ while (NextListSection != AnchorListSection) {
+
+ MIDL_user_free( NextListSection->Domains );
+
+ SectionToFree = NextListSection;
+ NextListSection = (PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION)
+ NextListSection->Links.Flink;
+ MIDL_user_free( SectionToFree );
+
+ }
+
+ LsapDbReleaseWriteLockTrustedDomainList( TrustedDomainList );
+ RtlDeleteResource( &TrustedDomainList->Resource );
+
+#if DBG
+ RtlZeroMemory(TrustedDomainList, sizeof(LSAP_DB_TRUSTED_DOMAIN_LIST) );
+#endif //DBG
+
+ }
+
+ return(Status);
+}
+
+
+
+BOOLEAN
+LsapDbIsValidTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if the Trusted Domain List is valid.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+Return Values:
+
+ BOOLEAN - TRUE if the list is valid, else FALSE
+
+--*/
+
+{
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ return(TrustedDomainList->Valid);
+}
+
+
+NTSTATUS
+LsapDbAcquireWriteLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ This function acquires the Write Lock for the Trusted Domain List.
+ No other readers or writers will be allowed to access the list while this
+ lock is held.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_UNSUCCESSFUL - The resource could not be acquired.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+
+ if (!RtlAcquireResourceExclusive( &TrustedDomainList->Resource, TRUE)) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto AcquireWriteLockTrustedDomainListError;
+ }
+
+AcquireWriteLockTrustedDomainListFinish:
+
+ return(Status);
+
+AcquireWriteLockTrustedDomainListError:
+
+ goto AcquireWriteLockTrustedDomainListFinish;
+}
+
+
+NTSTATUS
+LsapDbAcquireReadLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ )
+
+/*++
+
+Routine Descriptiion:
+
+ This function acquires the Read Lock for the Trusted Domain List.
+ No writers will be allowed to update the list while this lock is
+ held.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_UNSUCCESSFUL - The resource could not be acquired.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ if (!RtlAcquireResourceShared( &TrustedDomainList->Resource, TRUE)) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto AcquireReadLockTrustedDomainListError;
+ }
+
+AcquireReadLockTrustedDomainListFinish:
+
+ return(Status);
+
+AcquireReadLockTrustedDomainListError:
+
+ goto AcquireReadLockTrustedDomainListFinish;
+}
+
+
+VOID
+LsapDbReleaseWriteLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ This function releases the Write Lock for the Trusted Domain List.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ RtlReleaseResource( &TrustedDomainList->Resource );
+
+ return;
+}
+
+
+VOID
+LsapDbReleaseReadLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ )
+
+/*++
+
+Routine Description:
+
+ This function releases the Write Lock for the Trusted Domain List.
+
+Arguments:
+
+ TrustedDomainList - Specifies the Trusted Domain List to be
+ used. If NULL is specified, the Local Trusted Domain List
+ is assumed.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (TrustedDomainList == NULL) {
+
+ TrustedDomainList = &LsapDbTrustedDomainList;
+ }
+
+ RtlReleaseResource( &TrustedDomainList->Resource );
+
+ return;
+}
+
+
+NTSTATUS
+LsapDbBuildTrustedDomainCache(
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs a cache for the Trusted Domain objects. The
+ cache is a counted doubly linked list of blocks. Each block contains
+ a counted array of Trust Information entries, each Trusted Domain
+ appearing in the list just once.
+
+Arguments:
+
+ None
+
+Return Values:
+
+ None
+
+--*/
+
+{
+ return(LsapDbBuildTrustedDomainList( NULL, NULL ));
+
+}
+
+
+
diff --git a/private/lsa/server/dbhandle.c b/private/lsa/server/dbhandle.c
new file mode 100644
index 000000000..eadb8e2c9
--- /dev/null
+++ b/private/lsa/server/dbhandle.c
@@ -0,0 +1,994 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbhandle.c
+
+Abstract:
+
+ LSA Database Handle Manager
+
+ Access to an LSA database object involves a sequence of API calls
+ which involve the following:
+
+ o A call to an object-type dependent "open" API
+ o One or more calls to API that manipulate the object
+ o A call to the LsaClose API
+
+ It is necessary to track context for each open of an object, for example,
+ the accesses granted and the underlying LSA database handle to the
+ object. Lsa handles provide this mechanism: an Lsa handle is simply a
+ pointer to a data structure containing this context.
+
+Author:
+
+ Scott Birrell (ScottBi) May 29, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+//
+// Handle Table anchor. The handle table is just a linked list
+//
+
+struct _LSAP_DB_HANDLE LsapDbHandleTable;
+
+
+NTSTATUS
+LsapDbInitializeHandleTable()
+
+/*++
+
+Routine Description:
+
+ This function initializes the LSA Database Handle Table
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code. Currently, STATUS_SUCCESS is
+ the only result code returned.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Just make the statically declared handle point to itself
+ //
+
+ LsapDbHandleTable.Next = &LsapDbHandleTable;
+ LsapDbHandleTable.Previous = &LsapDbHandleTable;
+
+ //
+ // Initialize the count of open handles to 0
+ //
+
+ LsapDbState.OpenHandleCount = 0;
+ return(Status);
+}
+
+
+
+LSAPR_HANDLE
+LsapDbCreateHandle(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates and initializes a handle for an LSA Database object.
+ The handle is allocated from the LSA Heap and added to the handle table.
+ Using the Object Type, and either the Sid or Name provided in
+ ObjectInformation, the Logical and Physical Names of the object are
+ constructed and pointers to them are stored in the handle. The LSA
+ Database must be locked before calling this function.
+
+ If there is a Container Handle specified in the ObjectInformation, the
+ newly created handle inherits its trusted status (TRUE if trusted, else
+ FALSE). If there is no container handle, the trusted status is set
+ to FALSE by default. When a non-trusted handle is used to access an
+ object, impersonation and access validation occurs.
+
+Arguments:
+
+ ObjectInformation - Pointer to object information structure which must
+ have been validated by a calling routine. The following information
+ items must be specified:
+
+ o Object Type Id
+ o Object Logical Name (as ObjectAttributes->ObjectName, a pointer to
+ a Unicode string)
+ o Container object handle (for any object except the Policy object).
+ o Object Sid (if any)
+ All other fields in ObjectAttributes portion of ObjectInformation
+ such as SecurityDescriptor are ignored.
+
+ Options - Optional actions
+
+ LSAP_DB_TRUSTED - Handle is to be marked as Trusted.
+ handle is use, access checking will be bypassed. If the
+ handle is used to create or open a lower level object, that
+ object's handle will by default inherit the Trusted property.
+
+ LSAP_DB_NON_TRUSTED - Handle is to be marked as Non-Trusted.
+
+ If neither of the above options is specified, the handle will
+ either inherit the trusted status of the Container Handle
+ provilde in ObjectInformation, or, if none, the handle will
+ be marked non-trusted.
+
+Return Value:
+
+ If successful, the newly created handle is returned otherwise NULL
+ is returned.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE Handle = NULL;
+ PSID Sid = NULL;
+ ULONG SidLength;
+
+ if (!LsapDbIsLocked()) {
+
+ goto CreateHandleError;
+ }
+
+ //
+ // Allocate memory for the new handle from the process heap.
+ //
+
+ Handle = LsapAllocateLsaHeap(sizeof(struct _LSAP_DB_HANDLE));
+
+ if (Handle == NULL) {
+
+ goto CreateHandleError;
+ }
+
+ //
+ // Mark the handle as allocated and initialize the reference count
+ // to one. Initialize other fields based on the object information
+ // supplied.
+ //
+
+ Handle->Allocated = TRUE;
+ Handle->KeyHandle = NULL;
+ Handle->ReferenceCount = 1;
+ Handle->ObjectTypeId = ObjectInformation->ObjectTypeId;
+ Handle->ContainerHandle = (LSAP_DB_HANDLE) ObjectInformation->ObjectAttributes.RootDirectory;
+ Handle->Sid = NULL;
+ Handle->Trusted = FALSE;
+ Handle->DeletedObject = FALSE;
+ Handle->GenerateOnClose = FALSE;
+ Handle->Options = Options;
+ Handle->LogicalNameU.Buffer = NULL;
+ Handle->PhysicalNameU.Buffer = NULL;
+
+ //
+ // By default, the handle inherits the Trusted status of the
+ // container handle.
+ //
+
+ if (Handle->ContainerHandle != NULL) {
+
+ Handle->Trusted = Handle->ContainerHandle->Trusted;
+ }
+
+ //
+ // If Trusted/Non-Trusted status is explicitly specified, set the
+ // status to that specified.
+ //
+
+ if (Options & LSAP_DB_TRUSTED) {
+
+ Handle->Trusted = TRUE;
+
+ } else if (Options & LSAP_DB_NOT_TRUSTED) {
+
+ Handle->Trusted = FALSE;
+ }
+
+ //
+ // Capture the object's Logical and construct Physical Names from the
+ // Object Information and store them in the handle. These names are
+ // internal to the Lsa Database. Note that the input Logical Name
+ // cannot be directly stored in the handle because it will be in
+ // storage that is scoped only to the underlying server API call if
+ // the object for which this create handle is being done is of a type
+ // that is opened or created by name rather than by Sid.
+ //
+
+ Status = LsapDbGetNamesObject(
+ ObjectInformation,
+ &Handle->LogicalNameU,
+ &Handle->PhysicalNameU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateHandleError;
+ }
+
+ //
+ // Make a copy of the object's Sid and store pointer to it in
+ // the handle.
+ //
+
+ if (ObjectInformation->Sid != NULL) {
+
+ Sid = ObjectInformation->Sid;
+
+ if (!RtlValidSid( Sid )) {
+
+ goto CreateHandleError;
+ }
+
+ SidLength = RtlLengthSid( Sid );
+
+ Handle->Sid = LsapAllocateLsaHeap( SidLength );
+
+ if (Handle->Sid == NULL) {
+
+ goto CreateHandleError;
+ }
+
+ RtlCopySid( SidLength, Handle->Sid, Sid );
+ }
+
+ //
+ // Append the handle to the linked list
+ //
+
+ Handle->Next = LsapDbHandleTable.Next;
+ Handle->Previous = &LsapDbHandleTable;
+ Handle->Next->Previous = Handle;
+ Handle->Previous->Next = Handle;
+
+ //
+ // Increment the handle table count
+ //
+
+ LsapDbState.OpenHandleCount++;
+
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (Handle == (LSAP_DB_HANDLE) LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Creating global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+
+CreateHandleFinish:
+
+ return (Handle);
+
+CreateHandleError:
+
+ //
+ // If necessary, free the handle and contents.
+ //
+
+ if (Handle != NULL) {
+
+ //
+ // If a Sid was allocated, free it.
+ //
+
+ if (Handle->Sid != NULL) {
+
+ LsapFreeLsaHeap( Handle->Sid );
+ }
+
+ //
+ // If a Logical Name Buffer was allocated, free it.
+ //
+
+ if ((Handle->LogicalNameU.Length != 0) &&
+ (Handle->LogicalNameU.Buffer != NULL)) {
+
+ RtlFreeUnicodeString( &Handle->LogicalNameU );
+ }
+
+ //
+ // If a Physical Name Buffer was allocated, free it.
+ //
+
+ if ((Handle->PhysicalNameU.Length != 0) &&
+ (Handle->PhysicalNameU.Buffer != NULL)) {
+
+ RtlFreeUnicodeString( &Handle->PhysicalNameU );
+ }
+
+ //
+ // Free the handle itself.
+ //
+
+ LsapFreeLsaHeap( Handle );
+ Handle = NULL;
+ }
+
+ Handle = NULL;
+ goto CreateHandleFinish;
+}
+
+
+NTSTATUS
+LsapDbVerifyHandle(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ULONG Options,
+ IN LSAP_DB_OBJECT_TYPE_ID ExpectedObjectTypeId
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that a handle has a valid address and is of valid
+ format. The handle must be allocated and have a positive reference
+ count within the valid range. The object type id must be within range
+ and optionally equal to a specified type. The Lsa Database must be
+ locked before calling this function.
+
+Arguments:
+
+ ObjectHandle - Handle to be validated.
+
+ Options - Specifies optional actions to be taken
+
+ LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES - Allow handles for
+ deleted objects to pass the validation.
+
+ Other option flags may be specified. They will be ignored.
+
+ ExpectedObjectTypeId - Expected object type. If NullObject is
+ specified, the object type id is only range checked.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - Invalid address or handle contents
+--*/
+
+{
+ NTSTATUS Status = STATUS_INVALID_HANDLE;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;
+
+ //
+ // Verify that the Lsa Database is locked.
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // First verify that the handle's address is valid.
+ //
+
+ if (!LsapDbLookupHandle( ObjectHandle )) {
+
+ goto VerifyHandleError;
+ }
+
+ //
+ // Verify that the handle is allocated
+ //
+
+ if (!Handle->Allocated) {
+
+ goto VerifyHandleError;
+ }
+
+ //
+ // If the handle is marked as invalid, return an error unless
+ // these are admissible, e.g when validating for a close option
+ //
+
+ if (Handle->DeletedObject) {
+
+ if (!(Options & LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES)) {
+
+ goto VerifyHandleError;
+ }
+ }
+
+ //
+ // Verify that the handle contains a non-NULL handle to a Registry
+ // Key
+ //
+
+ if (Handle->KeyHandle == NULL) {
+
+ goto VerifyHandleError;
+ }
+
+ //
+ // Now either range-check or match the handle type
+ //
+
+ if (ExpectedObjectTypeId == NullObject) {
+
+ if ((Handle->ObjectTypeId < PolicyObject) ||
+ (Handle->ObjectTypeId >= DummyLastObject)) {
+
+ goto VerifyHandleError;
+ }
+
+ } else {
+
+ ASSERT (ExpectedObjectTypeId >= PolicyObject &&
+ ExpectedObjectTypeId < DummyLastObject);
+
+ if (Handle->ObjectTypeId != ExpectedObjectTypeId) {
+
+ goto VerifyHandleError;
+ }
+ }
+
+ //
+ // Verify that the handle's reference count is valid and positive
+ //
+
+ if (Handle->ReferenceCount == 0 ||
+ Handle->ReferenceCount > LSAP_DB_MAXIMUM_REFERENCE_COUNT) {
+
+ goto VerifyHandleError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+VerifyHandleFinish:
+
+ return(Status);
+
+VerifyHandleError:
+
+ goto VerifyHandleFinish;
+}
+
+
+BOOLEAN
+LsapDbLookupHandle(
+ IN LSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if a handle address is valid. The Lsa Database must
+ be locked before calling this function.
+
+Arguments:
+
+ ObjectHandle - handle to be validated.
+
+Return Value:
+
+ BOOLEAN - TRUE if handle is valid. FALSE if handle does not exist or
+ is invalid.
+
+--*/
+
+{
+ LSAP_DB_HANDLE ThisHandle;
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Simply do a linear scan of the small list of handles. Jazz this
+ // up later if needed.
+ //
+
+ for (ThisHandle = LsapDbHandleTable.Next;
+ ThisHandle != &LsapDbHandleTable;
+ ThisHandle = ThisHandle->Next) {
+
+ if (ThisHandle == (LSAP_DB_HANDLE) ObjectHandle) {
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+NTSTATUS
+LsapDbCloseHandle(
+ IN LSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function closes an LSA Handle. The memory for the handle is
+ freed. The LSA database must be locked before calling this function.
+
+ NOTE: Currently, handles do not have reference counts since they
+ are not shared among client threads.
+
+Arguments:
+
+ ObjectHandle - Handle to be closed.
+
+Return Value:
+
+ NTSTATUS - Return code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSAP_DB_HANDLE TempHandle;
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Verify that the handle exists. It may be marked invalid
+ //
+
+ Status = LsapDbVerifyHandle(
+ ObjectHandle,
+ LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES,
+ NullObject
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+ }
+
+ //
+ // Unhook the handle from the linked list
+ //
+
+ TempHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ TempHandle->Next->Previous = TempHandle->Previous;
+ TempHandle->Previous->Next = TempHandle->Next;
+
+ //
+ // Unlink the handle and free its memory
+ //
+
+ LsapDbFreeHandle( ObjectHandle );
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+LsapDbFreeHandle(
+ IN LSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function unlinks a handle and frees its memory. If the handle
+ contains a non-NULL Registry Key handle that handle is closed.
+
+Arguments:
+
+ ObjectHandle - handle to be freed.
+
+Return Value:
+
+ None. Any error is an internal error.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (ObjectHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+
+ //
+ // Free the Registry Key Handle (if any).
+ //
+
+ if (Handle->KeyHandle != NULL) {
+
+ Status = NtClose(Handle->KeyHandle);
+ ASSERT(NT_SUCCESS(Status));
+ Handle->KeyHandle = NULL;
+ }
+
+ //
+ // Mark the handle as not allocated.
+ //
+
+ Handle->Allocated = FALSE;
+
+ //
+ // Unlink the handle.
+ //
+
+ Handle->Next->Previous = Handle->Previous;
+ Handle->Previous->Next = Handle->Next;
+ Handle->Next = NULL;
+ Handle->Previous = NULL;
+
+
+ //
+ // Free fields of the handle
+ //
+
+ if (Handle->LogicalNameU.Buffer != NULL) {
+ RtlFreeUnicodeString( &Handle->LogicalNameU );
+ }
+ if (Handle->PhysicalNameU.Buffer != NULL) {
+ RtlFreeUnicodeString( &Handle->PhysicalNameU );
+ }
+ if (Handle->Sid != NULL) {
+ LsapFreeLsaHeap( Handle->Sid );
+ }
+
+ //
+ // Decrement the count of open handles.
+ //
+
+ ASSERT(LsapDbState.OpenHandleCount > 0);
+ LsapDbState.OpenHandleCount--;
+
+ //
+ // Free the handle structure itself
+
+ LsapFreeLsaHeap( ObjectHandle );
+}
+
+NTSTATUS
+LsapDbReferencesHandle(
+ IN LSAPR_HANDLE ObjectHandle,
+ OUT PULONG ReferenceCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the Reference Count for an object given a handle.
+ This is the sum of the Reference Counts found in each open handle. The
+ LSA Database must be locked before calling this function.
+
+Arguments:
+
+ ObjectHandle - Handle to the object.
+
+ ReferenceCount - Receives the Reference Count for the object.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - Specified handle is invalid.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE ThisHandle;
+ LSAP_DB_HANDLE Handle = ObjectHandle;
+ ULONG ReferenceCountToDate = 0;
+
+ //
+ // Verify that the handle is valid
+ //
+
+ Status = LsapDbVerifyHandle(
+ ObjectHandle,
+ LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES,
+ NullObject
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+ }
+
+ //
+ // Scan the Handle List looking for a match on object type.
+ //
+
+ ThisHandle = LsapDbHandleTable.Next;
+
+ while (ThisHandle != &LsapDbHandleTable) {
+
+ //
+ // Match on Object Type Id.
+ //
+
+ if (ThisHandle->ObjectTypeId == Handle->ObjectTypeId) {
+
+ //
+ // Object Type Id's match. If the Logical Names also
+ // match, add the Reference Count of the target handle
+ // to the total Reference Count obtained so far.
+ //
+
+ if (RtlEqualUnicodeString(
+ &(ThisHandle->LogicalNameU),
+ &(Handle->LogicalNameU),
+ FALSE
+ )) {
+
+ ReferenceCountToDate += ThisHandle->ReferenceCount;
+ }
+ }
+
+ ThisHandle = ThisHandle->Next;
+ }
+
+ *ReferenceCount = ReferenceCountToDate;
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbMarkDeletedObjectHandles(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN BOOLEAN MarkSelf
+ )
+
+/*++
+
+Routine Description:
+
+ This function invalidates open handles to an object. It is used
+ by object deletion code. Once an object has been deleted, the only
+ operation permitted on open handles remaining is to close them.
+
+Arguments:
+
+ ObjectHandle - Handle to an Lsa object.
+
+ MarkSelf - If TRUE, all handles to the object will be marked to
+ indicate that the object to which they relate has been deleted.
+ including the passed handle. If FALSE, all handles to the object
+ except the passed handle will be so marked.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_HANDLE ThisHandle;
+ LSAP_DB_HANDLE Handle = ObjectHandle;
+
+ ThisHandle = LsapDbHandleTable.Next;
+
+
+ while (ThisHandle != &LsapDbHandleTable) {
+
+ //
+ // Match on Object Type Id.
+ //
+
+ if (ThisHandle->ObjectTypeId == Handle->ObjectTypeId) {
+
+ //
+ // Object Type Id's match. If the Logical Names also
+ // match, invalidate the handle unless the handle is the
+ // passed one and we're to leave it valid.
+ //
+
+ if (RtlEqualUnicodeString(
+ &(ThisHandle->LogicalNameU),
+ &(Handle->LogicalNameU),
+ FALSE
+ )) {
+
+ if (MarkSelf || ThisHandle != (LSAP_DB_HANDLE) ObjectHandle) {
+
+ ThisHandle->DeletedObject = TRUE;
+ }
+ }
+ }
+
+ ThisHandle = ThisHandle->Next;
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbObjectNameFromHandle(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN BOOLEAN MakeCopy,
+ IN LSAP_DB_OBJECT_NAME_TYPE ObjectNameType,
+ OUT PLSAPR_UNICODE_STRING ObjectName
+ )
+
+/*++
+
+Routine Description:
+
+ This function retrieves a name from an Lsa Object Handle. The handle
+ is assumed to be valid and the Lsa Database lock should be held while
+ calling this function.
+
+Arguments
+
+ ObjectHandle - A handle to the object
+
+ MakeCopy - TRUE if a copy of the object name Unicode buffer is
+ to be allocated via MIDL_user_allocate, else FALSE.
+
+ ObjectNameType - Specifies the type of obejct name to be returned.
+
+ LsapDbObjectPhysicalName - Return the Physical Name
+ LsapDbObjectLogicalName - Return the Logical Name
+
+ ObjectName - Pointer to Unicode String structure which will be
+ initialized to point to the object name.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS The call completed successfully
+
+ STATUS_NO_MEMORY - Insufficient memory to allocate the buffer
+ for a copy of the object name when MakeCopy = TRUE.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ LSAPR_UNICODE_STRING OutputObjectName;
+ PLSAPR_UNICODE_STRING SourceName = NULL;
+
+ //
+ // Copy over the name.
+ //
+
+ switch (ObjectNameType) {
+
+ case LsapDbObjectPhysicalName:
+
+ SourceName = (PLSAPR_UNICODE_STRING) &InternalHandle->PhysicalNameU;
+ break;
+
+ case LsapDbObjectLogicalName:
+
+ SourceName = (PLSAPR_UNICODE_STRING) &InternalHandle->LogicalNameU;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ObjectNameFromHandleError;
+ }
+
+ OutputObjectName = *SourceName;
+
+ //
+ // If a copy was requested, allocate memory
+ //
+
+ if (MakeCopy) {
+
+ OutputObjectName.Buffer = MIDL_user_allocate( OutputObjectName.MaximumLength );
+
+ Status = STATUS_NO_MEMORY;
+
+ if (OutputObjectName.Buffer == NULL) {
+
+ goto ObjectNameFromHandleError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ RtlMoveMemory(
+ OutputObjectName.Buffer,
+ SourceName->Buffer,
+ SourceName->Length
+ );
+ }
+
+ *ObjectName = OutputObjectName;
+
+ObjectNameFromHandleFinish:
+
+ return(Status);
+
+ObjectNameFromHandleError:
+
+ ObjectName->Buffer = NULL;
+ ObjectName->Length = ObjectName->MaximumLength = 0;
+ goto ObjectNameFromHandleFinish;
+}
+
+
+VOID
+LsapDbDecrementReferenceCountHandle(
+ IN OUT LSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function decrements the reference count in a handle.
+
+Arguments:
+
+ ObjectHandle - Lsa Object handle
+
+Return Values:
+
+ None
+
+--*/
+
+{
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ InternalHandle->ReferenceCount--;
+ return;
+}
+
+VOID
+LsapDbMarkTrustedHandle(
+ IN OUT LSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function marks a handle as being Trusted. No access checking is
+ done on Trusted Handles, so only clients in the Security Process (lsass)
+ may use Trusted handles. The handle is assumed to be valid, and the Lsa
+ Database lock must be held while this function is called.
+
+Arguments:
+
+ ObjectHandle - Handle to be marked Trusted
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ InternalHandle->Trusted = TRUE;
+ return;
+}
+
+
diff --git a/private/lsa/server/dbinit.c b/private/lsa/server/dbinit.c
new file mode 100644
index 000000000..c72a87946
--- /dev/null
+++ b/private/lsa/server/dbinit.c
@@ -0,0 +1,2821 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbinit.c
+
+Abstract:
+
+ Local Security Authority - Database Server Initialization
+
+ This module contains functions which perform initialization of
+ the Database Server. Certain information is obtained from the
+ LSA database and is set up in global data for easy retrieval.
+
+Author:
+
+ Scott Birrell (ScottBi) July 25, 1991
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+//#define LSAP_DEBUG_MESSAGE_STRINGS
+
+#include <nt.h>
+#include "ntlsa.h"
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lsasrvp.h"
+#include "lsapmsgs.h"
+#include "dbp.h"
+
+
+
+
+NTSTATUS
+LsapDbBuildObjectCaches(
+ );
+
+NTSTATUS
+LsapAssignInitialHiveProtection(
+ HANDLE HiveRoot
+ );
+
+NTSTATUS
+LsapCreateDatabaseProtection(
+ PISECURITY_DESCRIPTOR Sd
+ );
+
+NTSTATUS
+LsapDbUpgradeRevision( VOID );
+
+
+
+NTSTATUS
+LsapDbInitializeServer(
+ IN ULONG Pass
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the LSA Database Server. The following
+ steps are performed:
+
+ o Initialize the LSA Database Lock
+ o Acquire the LSA Database Lock
+ o Initialize the Unicode Strings for the fixed names within the
+ LSA Database, e.g. LSA Database object attributes and well-known
+ object names.
+ o Initialize the Unicode Strings for the LSA Database Object constant
+ and well known names, e.g SubKeys, fixed object names.
+ o Initialize the Unicode Strings for LSA Object Containing Dirs
+ o Initialize the Generic Mappings for Database Object Types
+ o Initialize the Lsa Database Handle Table
+ o Install the LSA Database if necessary - Creates the Lsa Database
+ o and Manager account objects, and initializes the transaction
+ subtree
+ o Initialize the abs min, abs max and installation default quota limits
+ o Release the LSA Database Lock
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ BOOLEAN BooleanStatus = TRUE;
+ BOOLEAN AcquiredLock = FALSE;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
+ UNICODE_STRING ComputerName;
+ ULONG Length;
+ DWORD WinStatus;
+
+ //
+ // Initialize the LSA Database Lock and set it into the locked state
+ //
+
+ if (Pass == 1 ) {
+
+ LsapDbState.DbServerInitialized = FALSE;
+ Status = LsapDbInitializeLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+ }
+
+ //
+ // Acquire the LSA Database Lock. This allows subroutines to
+ // assert that the LSA Database is locked. Otherwise, it is
+ // not actually necessary, given that no other thread can access the
+ // LSA until initialization is complete.
+ //
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ AcquiredLock = TRUE;
+
+
+ if (Pass == 1) {
+
+ //
+ // Initialize the Unicode Strings for the fixed names within the
+ // LSA Database, e.g. LSA Database object attributes and well-known
+ // object names.
+ //
+
+ Status = LsapDbInitializeUnicodeNames();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Initialize the Unicode Strings for the Containing Directories for
+ // each LSA Database Object Type.
+ //
+
+ Status = LsapDbInitializeContainingDirs();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Initialize the LSA Subsystem name string. This is needed for
+ // NtAccessCheckAuditAlarm calls
+ //
+
+ RtlInitUnicodeString(&LsapState.SubsystemName, L"LSA");
+
+ //
+ // Initialize the Shutdown Pending state.
+ //
+
+ LsapState.SystemShutdownPending = FALSE;
+
+ //
+ // Initialize the Database Object Types. Information stored
+ // includes the Generic mappings and Object Counts.
+ //
+
+ Status = LsapDbInitializeObjectTypes();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Initialize the LSA Database Handle Table
+ //
+
+ Status = LsapDbInitializeHandleTable();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Open the LSA Database root Registry subkey. This stays
+ // open for use in adding transactions.
+ //
+
+ Status = LsapDbOpenRootRegistryKey();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Initialize the Lsa Database Cipher Key
+ //
+
+ Status = LsapDbInitializeCipherKey();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Initialize the LSA Database Transaction Subtree, creating it if
+ // one does not already exist. If the Transaction Subtree exists,
+ // commit any partially committed transaction if appropriate.
+ //
+
+ Status = RtlInitializeRXact(
+ LsapDbState.DbRootRegKeyHandle,
+ TRUE,
+ (PRTL_RXACT_CONTEXT *) &LsapDbState.RXactContext
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_RXACT_STATE_CREATED) {
+
+ LsapLogError(
+ "LsapDbInitializeServer: Registry Transaction Init returned 0x%lx\n",
+ Status
+ );
+
+ goto InitializeServerError;
+ }
+
+ LsapLogError(
+ "LsapDbInitializeServer: Registry Transaction State Did Not Exist\n",
+ Status
+ );
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Setup attributes for opening the Policy object.
+ //
+
+ ObjectInformation.ObjectTypeId = PolicyObject;
+ ObjectInformation.ContainerTypeId = 0;
+ ObjectInformation.Sid = NULL;
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ &LsapDbNames[Policy],
+ 0L,
+ NULL,
+ NULL
+ );
+
+ //
+ // Now try to open the root LSA Database object (Policy). This is a
+ // trusted call, so no access checking or impersonation will be done.
+ // Note that the handle obtained will remain open indefinitely. It is
+ // used after initialization for all internally generated accesses to
+ // the Policy object
+ //
+
+ Status = LsapDbOpenObject(
+ &ObjectInformation,
+ 0L,
+ LSAP_DB_TRUSTED,
+ &LsapDbHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // Open of LSA Database object failed. If any error other than
+ // object not found, there is a serious error which prevents the
+ // LSA from functioning, so abort.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ LsapLogError(
+ "LsapDbInitializeServer: Open failed 0x%lx\n"
+ "The Lsa Database must be reinstalled or manually\n"
+ "erased before using the system\n",
+ Status
+ );
+
+ goto InitializeServerError;
+ }
+
+ //
+ // The Lsa Database object was not found. Run the database installation
+ // routine so that people can boot without having to run the
+ // installation applet first.
+ //
+
+ LsapDatabaseSetupPerformed = TRUE;
+
+ Status = LsapDbInstallLsaDatabase(1);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+ }
+
+ //
+ // The Lsa Database object was successfully opened, possibly after
+ // having just been created. Proceed with the rest of server
+ // initialization. First, setup in-memory copies of the Installation
+ // Default, Absolute Min and Absolute Max system quota limits.
+ //
+
+ //
+ // Make the policy handle available throughout LSA
+ //
+
+ LsapPolicyHandle = LsapDbHandle;
+
+
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeServerError;
+ }
+
+
+ //
+ // Bring the database up to the current revision level,
+ // if necessary.
+ //
+
+ Status = LsapDbUpgradeRevision();
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeServerError;
+ }
+
+ //
+ // Initialize privilege object related code
+ //
+
+ Status = LsapDbInitializePrivilegeObject();
+
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeServerError;
+ }
+
+ //
+ // Perform initialization for the Replicator. Replications
+ // are still disabled at this point.
+ //
+
+ Status = LsapDbInitializeReplication();
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Initialize the data for the new APIs (user rights)
+ //
+
+ Status = LsapDbInitializeRights();
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ } else if (Pass == 2) {
+
+ BOOLEAN ExpectTrue;
+
+ //
+ // Perform the second stage of database initialization.
+ // This is the initialization that depends on the product type.
+ // First, get the product type. Note that the Product Type may
+ // have already been retrieved from a number of routines that
+ // may be called during early installation, including
+ // LsarSetInformationPolicy() and LsarCreateTrustedDomain().
+ //
+
+ ExpectTrue = RtlGetNtProductType(&LsapProductType);
+ ASSERT( ExpectTrue == TRUE );
+
+ //
+ // If necessary, install the rest of our database.
+ //
+
+ if (LsapDatabaseSetupPerformed == TRUE) {
+
+ Status = LsapDbInstallLsaDatabase(2);
+
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeServerError;
+ }
+ }
+
+ //
+ // If this is a Win Nt product, set the SAM Accounts Domain
+ // Name equal to the Computer Name, which may have been changed
+ // since the last boot.
+ //
+
+ if ((LsapProductType == NtProductWinNt) ||
+ (LsapProductType == NtProductServer)) {
+
+ Status = LsarQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeServerError;
+ }
+
+ Length = (ULONG) 0;
+
+ BooleanStatus = GetComputerNameW(
+ (LPWSTR) ComputerName.Buffer,
+ (LPDWORD) &Length
+ );
+
+ WinStatus = GetLastError();
+
+ if (WinStatus != ERROR_BUFFER_OVERFLOW) {
+
+ KdPrint(("LsapDbInitializeServer: Failed to get Computer Name Length\n"
+ "Using default MACHINENAME instead\n"));
+
+ RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME );
+ Length = (ULONG) ComputerName.Length;
+
+ } else if (Length <= 1) {
+
+ KdPrint(("LsapDbInitializeServer: Null Computer Name\n"
+ "Using default MACHINENAME instead\n"));
+
+ RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME );
+ Length = (ULONG) ComputerName.Length;
+
+ } else {
+
+ ComputerName.Length = (USHORT) ((Length - 1) * sizeof (WCHAR));
+ ComputerName.MaximumLength = (USHORT) (Length * sizeof(WCHAR));
+ ComputerName.Buffer = MIDL_user_allocate( ComputerName.MaximumLength );
+ }
+
+ if (!GetComputerNameW(
+ (LPWSTR) ComputerName.Buffer,
+ (LPDWORD) &Length
+ )) {
+
+ KdPrint(("LsapDbInitializeServer: Failed to get Computer Name\n"
+ "Using default MACHINENAME instead\n"));
+
+ RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME );
+ }
+
+ PolicyAccountDomainInfo->DomainName = *((PLSAPR_UNICODE_STRING) &ComputerName);
+
+ Status = LsarSetInformationPolicy(
+ LsapPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PLSAPR_POLICY_INFORMATION) PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+ }
+
+ //
+ // Perform initialization for Lookup Sids and Names, including
+ // initialization of the Trusted Domain List.
+ //
+
+ Status = LsapDbLookupInitialize();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeServerError;
+ }
+
+ //
+ // Load the object caches. Any that fail to load have caching
+ // permanently turned off.
+ //
+
+ IgnoreStatus = LsapDbBuildObjectCaches();
+
+ //
+ // Mark the Server as being completely initialized.
+ //
+
+ LsapDbState.DbServerInitialized = TRUE;
+ }
+
+InitializeServerFinish:
+
+ //
+ // If necessary, release the LSA Database Lock.
+ //
+
+ if (AcquiredLock) {
+
+ LsapDbReleaseLock();
+ }
+
+ return(Status);
+
+InitializeServerError:
+
+ goto InitializeServerFinish;
+}
+
+
+
+NTSTATUS
+LsapDbUpgradeRevision(
+ )
+
+/*++
+
+Routine Description:
+
+ This function brings the LSA policy database up to date if necessary.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS
+ Status;
+
+ ULONG
+ Revision,
+ RevisionLength = sizeof( ULONG );
+
+ LSAP_DB_ATTRIBUTE
+ Attributes[20];
+
+ PLSAP_DB_ATTRIBUTE
+ NextAttribute;
+
+ ULONG
+ AttributeCount = 0;
+
+ LARGE_INTEGER
+ ModifiedIdAtLastPromotion;
+
+ NextAttribute = Attributes;
+
+ //
+ // Read the Revision Info from the PolRevision attribute
+ // of the Policy object in the LSA Database.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[PolRevision],
+ (PVOID) &Revision,
+ &RevisionLength
+ );
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ //
+ // attribute doesn't exist.
+ // This means the database is an NT1.0 format.
+ // Upgrade it to the current revision.
+ //
+
+ Revision = LSAP_DB_REVISION_1_0;
+ Status = STATUS_SUCCESS;
+ }
+
+
+
+ if ( NT_SUCCESS(Status) &&
+ (Revision == LSAP_DB_REVISION_1_0) ) {
+
+ //
+ // Perform updates to bring revision up to 1.1
+ //
+
+ Revision = LSAP_DB_REVISION_1_1;
+
+ //
+ // Create the revision attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolRevision],
+ &Revision,
+ sizeof (ULONG),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+
+ //
+ // Create the ModifiedIdAtLastPromotion attribute
+ //
+
+ ModifiedIdAtLastPromotion =
+ RtlConvertUlongToLargeInteger( (ULONG) 1 );
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolPromot],
+ &ModifiedIdAtLastPromotion,
+ sizeof (LARGE_INTEGER),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Revision is now 1.1
+ //
+
+ }
+
+
+ //
+ // In the future, revision updates can be made
+ // by adding "if" blocks similar to the one above.
+ //
+ // Remember, however, that the attributes are pointing
+ // to values in local variables. Any local variable
+ // value changed before the attribute is written out
+ // will cause that attribute value to be changed.
+ //
+
+
+
+
+
+
+ //
+ // Now write out all attributes that have been added (if any)
+ //
+
+ if (AttributeCount > 0) {
+
+ Status = LsapDbReferenceObject(
+ LsapDbHandle,
+ 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsapDbWriteAttributesObject(
+ LsapDbHandle,
+ Attributes,
+ AttributeCount
+ );
+
+
+ Status = LsapDbDereferenceObject(
+ &LsapDbHandle,
+ PolicyObject,
+ (LSAP_DB_RELEASE_LOCK |
+ LSAP_DB_FINISH_TRANSACTION),
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+LsapDbBuildObjectCaches(
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds caches for Lsa objects. These caches contain a
+ subset of the information for some object types.
+
+Arguments:
+
+ None
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS IgnoreStatus;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+
+ //
+ // Initialize all the caches.
+ //
+
+ for (ObjectTypeId = PolicyObject;
+ ObjectTypeId <= SecretObject;
+ ObjectTypeId++) {
+
+ IgnoreStatus = LsapDbRebuildCache( ObjectTypeId );
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+LsapDbInitializeObjectTypes(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the information pertinent to each object
+ type in the LSA Database. This information includes the following:
+
+ o Generic Mapping Arrays
+
+ The Generic Mapping array for each object defines the list of
+ object-type-specific access types that correspond to the generic
+ access types GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE and
+ GENERIC_ALL for the object type.
+
+ o Object Count Information
+
+ The Object Count Information includes a count of the number of objects
+ that exist for each type, the upper limit on this number (if any) for
+ each object type, and the error code to return when that limit is
+ reached.
+
+ o Write Operation Masks
+
+ These specify which access types are update operations
+
+ o Default accesses granted to World and Admin aliases
+
+ o Invalid access masks for each object type
+
+ These masks specify the bits in an access mask that are invalid for
+ a given object type.
+
+ o Initial owners of each object type
+
+ o Object caching supported for each object type.
+
+
+
+Arguments:
+
+ None. The Generic Mapping arrays are held the LsapDbState structure.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code. Currently, there are no error
+ situations in this code, so STATUS_SUCCESS is always returned.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PGENERIC_MAPPING GenericMapping;
+ PLSAP_DB_OBJECT_TYPE ObjectType;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+
+ //
+ // Initialize the Generic Mapping Array for the PolicyObject Object Type
+ // Note that there is only one object of this type and objects of this
+ // type can neither be created nor destroyed.
+ //
+
+ GenericMapping =
+ &LsapDbState.DbObjectTypes[PolicyObject].GenericMapping;
+ GenericMapping->GenericRead =
+ STANDARD_RIGHTS_READ |
+ POLICY_VIEW_AUDIT_INFORMATION |
+ POLICY_GET_PRIVATE_INFORMATION;
+
+ GenericMapping->GenericWrite =
+ STANDARD_RIGHTS_WRITE |
+ POLICY_TRUST_ADMIN |
+ POLICY_CREATE_ACCOUNT |
+ POLICY_CREATE_SECRET |
+ POLICY_CREATE_PRIVILEGE |
+ POLICY_SET_DEFAULT_QUOTA_LIMITS |
+ POLICY_SET_AUDIT_REQUIREMENTS |
+ POLICY_AUDIT_LOG_ADMIN |
+ POLICY_SERVER_ADMIN;
+
+ GenericMapping->GenericExecute =
+ STANDARD_RIGHTS_EXECUTE |
+ POLICY_VIEW_LOCAL_INFORMATION |
+ POLICY_LOOKUP_NAMES;
+
+ GenericMapping->GenericAll = POLICY_ALL_ACCESS;
+
+ //
+ // Initialize the Generic Mapping Array for the Account Object Type
+ // Note that Account Objects can be created and destroyed.
+ //
+
+ GenericMapping =
+ &LsapDbState.DbObjectTypes[AccountObject].GenericMapping;
+
+ GenericMapping->GenericRead =
+ STANDARD_RIGHTS_READ |
+ ACCOUNT_VIEW;
+
+ GenericMapping->GenericWrite =
+ STANDARD_RIGHTS_WRITE |
+ ACCOUNT_ADJUST_PRIVILEGES |
+ ACCOUNT_ADJUST_QUOTAS |
+ ACCOUNT_ADJUST_SYSTEM_ACCESS;
+
+ GenericMapping->GenericExecute =
+ STANDARD_RIGHTS_EXECUTE;
+
+ GenericMapping->GenericAll = ACCOUNT_ALL_ACCESS;
+
+ //
+ // Initialize the Generic Mapping Array for the TrustedDomain Object
+ // Type.
+ //
+
+ GenericMapping =
+ &LsapDbState.DbObjectTypes[TrustedDomainObject].GenericMapping;
+
+ GenericMapping->GenericRead =
+ STANDARD_RIGHTS_READ |
+ TRUSTED_QUERY_DOMAIN_NAME;
+
+ GenericMapping->GenericWrite =
+ STANDARD_RIGHTS_WRITE |
+ TRUSTED_SET_CONTROLLERS |
+ TRUSTED_SET_POSIX;
+
+ GenericMapping->GenericExecute =
+ STANDARD_RIGHTS_EXECUTE |
+ TRUSTED_QUERY_CONTROLLERS |
+ TRUSTED_QUERY_POSIX;
+
+ GenericMapping->GenericAll = TRUSTED_ALL_ACCESS;
+
+ //
+ // Initialize the Generic Mapping Array for the Secret Object
+ // Type.
+ //
+
+ GenericMapping =
+ &LsapDbState.DbObjectTypes[SecretObject].GenericMapping;
+
+ GenericMapping->GenericRead =
+ STANDARD_RIGHTS_READ |
+ SECRET_QUERY_VALUE;
+
+ GenericMapping->GenericWrite =
+ STANDARD_RIGHTS_WRITE |
+ SECRET_SET_VALUE;
+
+ GenericMapping->GenericExecute =
+ STANDARD_RIGHTS_EXECUTE;
+
+ GenericMapping->GenericAll = SECRET_ALL_ACCESS;
+
+ //
+ // Initialize the Object Count Information to defaults
+ //
+
+ ObjectType = &(LsapDbState.DbObjectTypes[PolicyObject]);
+
+ for (ObjectTypeId = PolicyObject;
+ ObjectTypeId < DummyLastObject;
+ ObjectTypeId++) {
+
+ ObjectType->ObjectCount = 0;
+ ObjectType->ObjectCountLimited = FALSE;
+ ObjectType->ObjectCountError = STATUS_SUCCESS;
+ ObjectType->MaximumObjectCount = 0;
+ }
+
+ //
+ // Set specific limits for Secret Object Type. This is the only
+ // object type so far to have limits.
+ //
+
+ ObjectType = &(LsapDbState.DbObjectTypes[SecretObject]);
+ ObjectType->ObjectCountLimited = TRUE;
+ ObjectType->ObjectCountError = STATUS_TOO_MANY_SECRETS;
+ ObjectType->MaximumObjectCount = LSA_SECRET_MAXIMUM_COUNT;
+
+ //
+ // Initialize the write operations for each object type
+ //
+
+ LsapDbState.DbObjectTypes[PolicyObject].WriteOperations = LSAP_POLICY_WRITE_OPS;
+ LsapDbState.DbObjectTypes[TrustedDomainObject].WriteOperations = LSAP_TRUSTED_WRITE_OPS;
+ LsapDbState.DbObjectTypes[AccountObject].WriteOperations = LSAP_ACCOUNT_WRITE_OPS;
+ LsapDbState.DbObjectTypes[SecretObject].WriteOperations = LSAP_SECRET_WRITE_OPS;
+
+ //
+ // Initialize the default accesses granted to Domain Admins alias
+ //
+
+ LsapDbState.DbObjectTypes[PolicyObject].AliasAdminsAccess = GENERIC_ALL;
+ LsapDbState.DbObjectTypes[TrustedDomainObject].AliasAdminsAccess = GENERIC_ALL | DELETE;
+ LsapDbState.DbObjectTypes[AccountObject].AliasAdminsAccess = GENERIC_ALL | DELETE;
+ LsapDbState.DbObjectTypes[SecretObject].AliasAdminsAccess = GENERIC_ALL | DELETE;
+
+ //
+ // Initialize the default accesses granted to World alias
+ //
+
+ LsapDbState.DbObjectTypes[PolicyObject].WorldAccess = GENERIC_EXECUTE;
+ LsapDbState.DbObjectTypes[TrustedDomainObject].WorldAccess = GENERIC_EXECUTE;
+ LsapDbState.DbObjectTypes[AccountObject].WorldAccess = GENERIC_EXECUTE;
+ LsapDbState.DbObjectTypes[SecretObject].WorldAccess = GENERIC_EXECUTE;
+
+ //
+ // Initialize the Invalid Access masks for each object type
+ //
+
+ LsapDbState.DbObjectTypes[PolicyObject].InvalidMappedAccess =
+ ((ACCESS_MASK)(~(POLICY_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
+ LsapDbState.DbObjectTypes[TrustedDomainObject].InvalidMappedAccess =
+ ((ACCESS_MASK)(~(TRUSTED_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
+ LsapDbState.DbObjectTypes[AccountObject].InvalidMappedAccess =
+ ((ACCESS_MASK)(~(ACCOUNT_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
+ LsapDbState.DbObjectTypes[SecretObject].InvalidMappedAccess =
+ ((ACCESS_MASK)(~(SECRET_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
+
+ //
+ // Initialize the Initial Owners for new objects of each type
+ //
+
+ LsapDbState.DbObjectTypes[PolicyObject].InitialOwnerSid = LsapAliasAdminsSid;
+ LsapDbState.DbObjectTypes[TrustedDomainObject].InitialOwnerSid = LsapAliasAdminsSid;
+ LsapDbState.DbObjectTypes[AccountObject].InitialOwnerSid = LsapAliasAdminsSid;
+ LsapDbState.DbObjectTypes[SecretObject].InitialOwnerSid = LsapAliasAdminsSid;
+
+ //
+ // Specify method of access to objects of the type. Currently, all objects
+ // of a given type are accessed in the same way, either by Sid or by Name
+ // but not both.
+ //
+
+ LsapDbState.DbObjectTypes[PolicyObject].AccessedByName = TRUE;
+ LsapDbState.DbObjectTypes[TrustedDomainObject].AccessedByName = FALSE;
+ LsapDbState.DbObjectTypes[AccountObject].AccessedByName = FALSE;
+ LsapDbState.DbObjectTypes[SecretObject].AccessedByName = TRUE;
+
+ LsapDbState.DbObjectTypes[PolicyObject].AccessedBySid = FALSE;
+ LsapDbState.DbObjectTypes[TrustedDomainObject].AccessedBySid = TRUE;
+ LsapDbState.DbObjectTypes[AccountObject].AccessedBySid = TRUE;
+ LsapDbState.DbObjectTypes[SecretObject].AccessedBySid = FALSE;
+
+ //
+ // Specify the object types for which caching is supported (in full
+ // or in part) and turn caching off initially for all object types.
+ // Object types for which caching is supported have ther caches set
+ // to the "Invalid" state. Automatic restore is allowed for caches
+ // in this state. Object types for which caching is not supported
+ // are set to the "Not supported" state. Note that a cache is
+ // also placed in the "not supported" state if an attempt to restore
+ // it fails.
+ //
+
+ LsapDbMakeCacheInvalid( PolicyObject );
+ LsapDbMakeCacheInvalid( TrustedDomainObject );
+ LsapDbMakeCacheInvalid( AccountObject );
+ LsapDbMakeCacheUnsupported( SecretObject );
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbInitializeUnicodeNames()
+
+/*++
+
+Routine Description:
+
+ This function initializes two arrays of Unicode Strings. The
+ LsapDbNames array contains Unicode Strings for all of the constant
+ names in the Lsa Database. The LsapDbObjectTypeNames is indexed
+ by Object Type Id and contains the Unicode Strings for all of the
+ LSA Database object types.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_NAMES Index;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+
+ PCWSTR UnicodeNames[DummyLastName + 1] = {
+
+ L"SecDesc",
+ L"Privilgs",
+ L"Sid",
+ L"Name",
+ L"AdminMod",
+ L"OperMode",
+ L"QuotaLim",
+ L"DefQuota",
+ L"QuAbsMin",
+ L"QuAbsMax",
+ L"AdtLog",
+ L"AdtEvent",
+ L"PrDomain",
+ L"EnPasswd",
+ L"Policy",
+ L"Accounts",
+ L"Domains",
+ L"Secrets",
+ L"CurrVal",
+ L"OldVal",
+ L"CupdTime",
+ L"OupdTime",
+ L"WkstaMgr",
+ L"PolAdtLg",
+ L"PolAdtEv",
+ L"PolAcDmN",
+ L"PolAcDmS",
+ L"PolPrDmN",
+ L"PolPrDmS",
+ L"PolPdAcN",
+ L"PolSrvRo",
+ L"PolRepSc",
+ L"PolRepAc",
+ L"PolRevision",
+ L"PolDefQu",
+ L"PolMod",
+ L"PolPromot",
+ L"PolAdtFL",
+ L"PolState",
+ L"PolNxPxf",
+ L"ActSysAc",
+ L"TrDmName",
+ L"TrDmSid",
+ L"TrDmAcN",
+ L"TrDmCtN",
+ L"TrDmPxOf",
+ L"TrDmCtEn",
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Eventlog\\Security",
+ L"MaxSize",
+ L"Retention",
+ L"DummyLastName"
+ };
+
+ PCWSTR UnicodeObjectTypeNames[DummyLastObject] = {
+
+ L"NullObject",
+ L"PolicyObject",
+ L"TrustedDomainObject",
+ L"UserAccountObject",
+ L"SecretObject"
+ };
+
+ //
+ // Initialize general array of Unicode Names
+ //
+
+ for (Index = SecDesc; Index < DummyLastName; Index++) {
+
+ RtlInitUnicodeString( &LsapDbNames[Index], UnicodeNames[Index] );
+ }
+
+ //
+ // Initialize array of Unicode Names for Lsa Database Object Types
+ //
+
+ for (ObjectTypeId = NullObject;
+ ObjectTypeId < DummyLastObject;
+ ObjectTypeId++) {
+
+ RtlInitUnicodeString(
+ &LsapDbObjectTypeNames[ObjectTypeId],
+ UnicodeObjectTypeNames[ObjectTypeId]
+ );
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbInitializeContainingDirs()
+
+/*++
+
+Routine Description:
+
+ This function initializes Unicode strings for the names of the Containing
+ directories for each object type. The Containing Directory is the
+ Registry Key under which all objects of the given type are created and is
+ relative to the LSA Database root. Note that objects of a given type all
+ exist under a single Registry node, that is, the type of an object
+ uniquely determines the name of its containing directory.
+
+ NOTE: Containing Directories are used to produce Physical Object Names
+ from Logical Object Names. The Physical Object Name is simply
+ the Logical Object Name prepended with the Containing Directory
+ Name and a "\".
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+
+ PWSTR ContainingDirectories[DummyLastObject] = {
+
+ L"",
+ L"",
+ L"Domains",
+ L"Accounts",
+ L"Secrets"
+ };
+
+ //
+ // Initialize the array of Unicode Strings indexed by object type setting
+ // the Containing Directory name for each object type.
+ //
+
+ for (ObjectTypeId = PolicyObject;
+ ObjectTypeId < DummyLastObject;
+ ObjectTypeId++) {
+
+ RtlInitUnicodeString(
+ &LsapDbContDirs[ObjectTypeId],
+ ContainingDirectories[ ObjectTypeId ]
+ );
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbInitializeDefaultQuotaLimits(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes three different sets of Quota Limits in
+ global data.
+
+ o The System Installation Default Quota Limits. These are read from
+ the Policy Object's "DefQuota" attribute.
+ o The Absolute Minimum quota limit values that may be set
+ o The Absolute Maximum quota limit values that may be set
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG QuotaLimitsLength = sizeof (QUOTA_LIMITS);
+
+ //
+ // Read the installed System Default Quotas from the DefQuota attribute
+ // of the Policy object in the LSA Database.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[DefQuota],
+ (PVOID) &LsapDbInstalledQuotaLimits,
+ &QuotaLimitsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbInitializeDefaultQuotaLimits: Read DefQuota Attribute returned 0x%lx\n",
+ Status
+ );
+
+ goto InitializeDefaultQuotaLimitsError;
+ }
+
+ //
+ // Read the System Abs Min Quotas from the QuAbsMin attribute
+ // of the Policy object in the LSA Database.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[QuAbsMin],
+ (PVOID) &LsapDbAbsMinQuotaLimits,
+ &QuotaLimitsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbInitializeDefaultQuotaLimits: Read QuAbsMin Attribute returned 0x%lx\n",
+ Status
+ );
+
+ goto InitializeDefaultQuotaLimitsError;
+ }
+
+ //
+ // Read the System Abs Max Quotas from the QuAbsMax attribute
+ // of the Policy object in the LSA Database.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[QuAbsMax],
+ (PVOID) &LsapDbAbsMaxQuotaLimits,
+ &QuotaLimitsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbInitializeDefaultQuotaLimits: Read QuAbsMax Attribute returned 0x%lx\n",
+ Status
+ );
+
+ goto InitializeDefaultQuotaLimitsError;
+ }
+
+InitializeDefaultQuotaLimitsFinish:
+
+ return(Status);
+
+InitializeDefaultQuotaLimitsError:
+
+ goto InitializeDefaultQuotaLimitsFinish;
+}
+
+
+NTSTATUS
+LsapDbInitializeReplication(
+ )
+
+/*++
+
+Routine Description:
+
+ This function performes LSA initialization for replication and turns
+ on notification of LSA Database updates to the LSA Database Replicator.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG PolicyModificationInfoLength = sizeof (POLICY_MODIFICATION_INFO);
+ ULONG PolicyLsaServerRoleInfoLength = sizeof(POLICY_LSA_SERVER_ROLE_INFO);
+ ULONG LargeIntegerLength = sizeof( LARGE_INTEGER );
+
+ //
+ // Read the Policy Modification Info from the PolMod attribute
+ // of the Policy object in the LSA Database.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[PolMod],
+ (PVOID) &LsapDbState.PolicyModificationInfo,
+ &PolicyModificationInfoLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeReplicationError;
+ }
+
+ //
+ // Read the Modified at last promtion info from the PolPromot attribute
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[PolPromot],
+ (PVOID) &LsapDbState.ModifiedIdAtLastPromotion,
+ &LargeIntegerLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeReplicationError;
+ }
+
+ //
+ // Read the Policy Server Role Info from the PolSrvRo attribute
+ //
+
+ Status = LsapDbReadAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[PolSrvRo],
+ (PVOID) &LsapDbState.PolicyLsaServerRoleInfo,
+ &PolicyLsaServerRoleInfoLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto InitializeReplicationError;
+ }
+
+ //
+ // Now notify the Replicator of the Server Role.
+ //
+
+ Status = LsapDbNotifyRoleChangePolicy(
+ LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ if ( Status == STATUS_DLL_NOT_FOUND ) {
+ Status = STATUS_SUCCESS;
+ }
+ goto InitializeReplicationError;
+ }
+
+
+InitializeReplicationFinish:
+
+ return(Status);
+
+InitializeReplicationError:
+
+ goto InitializeReplicationFinish;
+}
+
+
+NTSTATUS
+LsapDbInitializeCipherKey(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the LSA Database Cipher Key.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING CipherKeyU;
+ LSAP_CR_CLEAR_VALUE ClearCipherKey;
+ PLSAP_CR_CIPHER_VALUE CipherCipherKey;
+
+ LsapDbCipherKey = NULL;
+
+ //
+ // Initialize the Cipher key to a hardwired constant
+ // encrypted with itself.
+ //
+
+ RtlInitUnicodeString( &CipherKeyU, L"823543" );
+
+ LsapCrUnicodeToClearValue(&CipherKeyU, &ClearCipherKey);
+
+ Status = LsapCrEncryptValue(
+ &ClearCipherKey,
+ (PLSAP_CR_CIPHER_KEY) &ClearCipherKey,
+ &CipherCipherKey
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbInitializeReplication: NtQuerySystemTime returned 0x%lx\n",
+ Status
+ );
+
+ goto InitializeCipherKeyError;
+ }
+
+ LsapDbCipherKey = (PLSAP_CR_CIPHER_KEY) CipherCipherKey;
+
+InitializeCipherKeyFinish:
+
+ return(Status);
+
+InitializeCipherKeyError:
+
+ goto InitializeCipherKeyFinish;
+}
+
+
+NTSTATUS
+LsapDbOpenRootRegistryKey(
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens the LSA Database Root Registry Key. This has
+ the fixed name \Registry\Machine\Security.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING DbRootRegKeyNameU;
+ OBJECT_ATTRIBUTES DbAttributes;
+
+ RtlInitUnicodeString( &DbRootRegKeyNameU, LSAP_DB_ROOT_REG_KEY_NAME );
+
+ InitializeObjectAttributes(
+ &DbAttributes,
+ &DbRootRegKeyNameU,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = RtlpNtOpenKey(
+ (PHANDLE) &LsapDbState.DbRootRegKeyHandle,
+ (KEY_READ | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | WRITE_DAC),
+ &DbAttributes,
+ 0
+ );
+
+
+ if (!NT_SUCCESS(Status)) {
+ LsapLogError(
+ "LsapDbOpenRootRegistryKey: Open Root Key for LSA Policy Database returned 0x%lx\n",
+ Status
+ );
+ goto OpenRootRegistryKeyError;
+ }
+
+
+ //
+ // If there are no sub-keys, then we are in system-install.
+ // Assign the initial protection of this hive.
+ //
+
+ Status = LsapAssignInitialHiveProtection( LsapDbState.DbRootRegKeyHandle );
+
+ if (!NT_SUCCESS(Status)) {
+ LsapLogError(
+ "LsapDbOpenRootRegistryKey: Couldn't assign initial hive protection 0x%lx\n",
+ Status
+ );
+ goto OpenRootRegistryKeyError;
+ }
+
+
+
+OpenRootRegistryKeyFinish:
+
+ return(Status);
+
+OpenRootRegistryKeyError:
+
+ goto OpenRootRegistryKeyFinish;
+}
+
+
+NTSTATUS
+LsapAssignInitialHiveProtection(
+ HANDLE HiveRoot
+ )
+
+
+/*++
+
+Routine Description:
+
+ This function assigns inheritable protection to the hive root key.
+ It will only do this if the hive root has no sub-keys.
+ This condition will only exist during system installation.
+
+ WARNING -
+
+ THIS ROUTINE IS TAILORED TO OPERATE ON THE \REGISTRY\SECURITY HIVE.
+ As such, it expects the Root key to have exactly one sub-key (a
+ link to the SAM hive) if the the database has NOT been initialized.
+ Otherwise, it expects the LSA policy database keys to be present.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - Everything went fine. No indication of whether
+ protection was necessarily assigned or not.
+
+ All other status values are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS
+ Status;
+
+ KEY_BASIC_INFORMATION
+ DummyBuffer;
+
+ ULONG
+ IgnoreRequiredLength;
+
+ SECURITY_DESCRIPTOR
+ Sd;
+
+
+ //
+ // See if the hive has more than 1 sub-keys.
+ //
+ //
+
+ Status = NtEnumerateKey(
+ HiveRoot,
+ 1, // Index - 0 is the SAM link, 1 is LSA policy database stuff
+ KeyBasicInformation, // Name of key
+ &DummyBuffer,
+ sizeof(DummyBuffer),
+ &IgnoreRequiredLength
+ );
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+
+ //
+ // We are initializing the system...
+ // Apply a reasonable ACL to the hive root.
+ //
+
+ Status = LsapCreateDatabaseProtection( &Sd );
+
+ if (NT_SUCCESS(Status)) {
+ Status = NtSetSecurityObject(
+ HiveRoot, // Object to apply to
+ DACL_SECURITY_INFORMATION, // Information to set
+ (PSECURITY_DESCRIPTOR)&Sd // Descriptor
+ );
+ }
+ } else {
+
+ Status = STATUS_SUCCESS;
+ }
+
+ return(Status);
+
+}
+
+
+
+NTSTATUS
+LsapCreateDatabaseProtection(
+ PISECURITY_DESCRIPTOR Sd
+ )
+
+
+/*++
+
+Routine Description:
+
+ This function allocates and initializes protection to assign to
+ the SAM database.
+
+ Upon return, any non-zero pointers in the security descriptors
+ point to memory allocated from process heap. It is the caller's
+ responsibility to free this memory.
+
+
+ Protection is:
+
+ System: All Access
+ Admin: ReadControl | WriteDac
+
+Arguments:
+
+ Sd - Address of a security descriptor to initialize.
+
+Return Value:
+
+ STATUS_SUCCESS - The Security descriptor has been initialize.
+
+ STATUS_NO_MEMORY - couldn't allocate memory for the protection info.
+
+--*/
+
+
+{
+ NTSTATUS
+ Status;
+
+ ULONG
+ Length;
+
+ USHORT
+ i;
+
+ PACL
+ Dacl;
+
+ PACE_HEADER
+ Ace;
+
+
+ //
+ // Initialize the security descriptor.
+ // This call should not fail.
+ //
+
+ Status = RtlCreateSecurityDescriptor( Sd, SECURITY_DESCRIPTOR_REVISION1 );
+ ASSERT(NT_SUCCESS(Status));
+
+ Length = (ULONG)sizeof(ACL) +
+ (2*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
+ RtlLengthSid( LsapLocalSystemSid ) +
+ RtlLengthSid( LsapAliasAdminsSid ) +
+ 8; // The 8 is just for good measure
+
+
+ Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
+
+ if (Dacl == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+
+ Status = RtlCreateAcl (Dacl, Length, ACL_REVISION2 );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Add ACEs to the ACL...
+ // These calls should not be able to fail.
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ Dacl,
+ ACL_REVISION2,
+ (GENERIC_ALL ),
+ LsapLocalSystemSid
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = RtlAddAccessAllowedAce(
+ Dacl,
+ ACL_REVISION2,
+ (READ_CONTROL | WRITE_DAC),
+ LsapAliasAdminsSid
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Now mark the ACEs as inheritable...
+ //
+
+ for ( i=0; i<Dacl->AceCount; i++) {
+
+ //
+ // Get the address of the next ACE
+ // (Shouldn't fail)
+ //
+
+ Status = RtlGetAce( Dacl, (ULONG)i, &Ace );
+ ASSERT(NT_SUCCESS(Status));
+
+ Ace->AceFlags |= (CONTAINER_INHERIT_ACE);
+
+ }
+
+
+ //
+ // And add the ACL to the security descriptor.
+ // This call should not fail.
+ //
+
+ Status = RtlSetDaclSecurityDescriptor(
+ Sd,
+ TRUE, // DaclPresent
+ Dacl, // Dacl OPTIONAL
+ FALSE // DaclDefaulted OPTIONAL
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+NTSTATUS
+LsapDbInitializeLock(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the LSA Database Lock. It is called once
+ only, during LSA Database initialization.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = RtlInitializeCriticalSection(&LsapDbState.DbLock);
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbInitializeLock: RtlInit..CritSec returned 0x%lx\n",
+ Status
+ );
+
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbInitializeWellKnownValues(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the well-known values used by LSA.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN b;
+
+ //
+ // Initialize the Well Known Sids
+ //
+
+ b = LsaIInitializeWellKnownSids( &WellKnownSids );
+
+ if (!b ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto InitializeWellKnownValuesError;
+ }
+
+ //
+ // Initialize the well known privilege values
+ //
+
+ Status = LsapDbInitializeWellKnownPrivs();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializeWellKnownValuesError;
+ }
+
+InitializeWellKnownValuesFinish:
+
+ return(Status);
+
+InitializeWellKnownValuesError:
+
+ goto InitializeWellKnownValuesFinish;
+}
+
+
+NTSTATUS
+LsapDbInitializeWellKnownPrivs(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the well-known privilege values.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Currently, only STATUS_SUCCESS is returned.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ LsapCreateTokenPrivilege =
+ RtlConvertLongToLuid(SE_CREATE_TOKEN_PRIVILEGE);
+ LsapAssignPrimaryTokenPrivilege =
+ RtlConvertLongToLuid(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE);
+ LsapLockMemoryPrivilege =
+ RtlConvertLongToLuid(SE_LOCK_MEMORY_PRIVILEGE);
+ LsapIncreaseQuotaPrivilege =
+ RtlConvertLongToLuid(SE_INCREASE_QUOTA_PRIVILEGE);
+ LsapUnsolicitedInputPrivilege =
+ RtlConvertLongToLuid(SE_UNSOLICITED_INPUT_PRIVILEGE);
+ LsapTcbPrivilege =
+ RtlConvertLongToLuid(SE_TCB_PRIVILEGE);
+ LsapSecurityPrivilege =
+ RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE);
+ LsapTakeOwnershipPrivilege =
+ RtlConvertLongToLuid(SE_TAKE_OWNERSHIP_PRIVILEGE);
+
+ return(Status);
+}
+
+
+BOOLEAN
+LsaIInitializeWellKnownSids(
+ OUT PLSAP_WELL_KNOWN_SID_ENTRY *WellKnownSids
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the Well-Known Sids
+
+Arguments:
+
+ WellKnownSids - Receives a pointer to a newly created table of
+ the Well Known Sids.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, else FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
+ ULONG SubAuthorities[LSAP_WELL_KNOWN_MAX_SUBAUTH_LEVEL];
+ ULONG OutputWellKnownSidsLength;
+ PLSAP_WELL_KNOWN_SID_ENTRY OutputWellKnownSids = NULL;
+ UNICODE_STRING SidName, NtAuthorityName;
+ HMODULE StringsResource;
+
+
+ //
+ // Get the message resource we need to get the SID names from
+ //
+
+ StringsResource = (HMODULE) LoadLibrary( L"LSASRV.DLL" );
+ if (StringsResource == NULL) {
+ return(FALSE);
+ }
+
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_NT_AUTHORITY,
+ &NtAuthorityName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+ //
+ // Allocate memory for the table of Sids.
+ //
+
+ OutputWellKnownSidsLength =
+ LsapDummyLastSidIndex * sizeof(LSAP_WELL_KNOWN_SID_ENTRY);
+
+ OutputWellKnownSids = RtlAllocateHeap(
+ RtlProcessHeap(),
+ 0,
+ OutputWellKnownSidsLength
+ );
+
+ if (OutputWellKnownSids == NULL) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ //
+ // Allocate and initialize the universal SIDs
+ //
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_NULL,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_NULL_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapNullSidIndex,
+ &LsapNullSidAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ L"",
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_WORLD,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_WORLD_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapWorldSidIndex,
+ &LsapWorldSidAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ L"",
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_LOCAL,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_LOCAL_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapLocalSidIndex,
+ &LsapLocalSidAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ L"",
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_CREATOR_OWNER,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_CREATOR_OWNER_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapCreatorOwnerSidIndex,
+ &LsapCreatorSidAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ L"",
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_CREATOR_GROUP,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_CREATOR_GROUP_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapCreatorGroupSidIndex,
+ &LsapCreatorSidAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ L"",
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_CREATOR_OWNER_SERVER,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_CREATOR_OWNER_SERVER_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapCreatorOwnerServerSidIndex,
+ &LsapCreatorSidAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ L"",
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_CREATOR_GROUP_SERVER,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_CREATOR_GROUP_SERVER_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapCreatorGroupServerSidIndex,
+ &LsapCreatorSidAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ L"",
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+
+ //
+ // Initialize the Nt well-known Sids
+ //
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_NT_DOMAIN,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapNtAuthoritySidIndex,
+ &LsapNtAuthority,
+ 0,
+ NULL,
+ L"",
+ SidName.Buffer,
+ SidTypeDomain
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_DIALUP,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_DIALUP_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapDialupSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_NETWORK,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_NETWORK_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapNetworkSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_BATCH,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_BATCH_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapBatchSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_INTERACTIVE,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_INTERACTIVE_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapInteractiveSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_SERVICE,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+ SubAuthorities[0] = SECURITY_SERVICE_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapServiceSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_ANONYMOUS,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_ANONYMOUS_LOGON_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapAnonymousSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_SERVER,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_SERVER_LOGON_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapServerSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ //
+ // Add any Logon Id well known sids here.
+ //
+
+ SubAuthorities[0] = SECURITY_LOGON_IDS_RID;
+ SubAuthorities[1] = 0;
+ SubAuthorities[2] = 0;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapLogonSidIndex,
+ &LsapNtAuthority,
+ 3,
+ SubAuthorities,
+ L"",
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_BUILTIN,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_BUILTIN_DOMAIN_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapBuiltInDomainSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ L"",
+ SidName.Buffer,
+ SidTypeDomain
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_SYSTEM,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_LOCAL_SYSTEM_RID;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapLocalSystemSidIndex,
+ &LsapNtAuthority,
+ 1,
+ SubAuthorities,
+ SidName.Buffer,
+ NtAuthorityName.Buffer,
+ SidTypeWellKnownGroup
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+
+ //
+ // JK: Why isn't the following Well-Known SID name "Administrators"?
+ //
+
+ Status = LsapGetMessageStrings(
+ StringsResource,
+ LSAP_SID_NAME_BUILTIN,
+ &SidName,
+ 0,
+ NULL
+ ); ASSERT(NT_SUCCESS(Status));
+
+ SubAuthorities[0] = SECURITY_BUILTIN_DOMAIN_RID;
+ SubAuthorities[1] = DOMAIN_ALIAS_RID_ADMINS;
+
+ if (!LsaIInitializeWellKnownSid(
+ OutputWellKnownSids,
+ LsapAliasAdminsSidIndex,
+ &LsapNtAuthority, // ScottBi - why is this NT authority?
+ 2,
+ SubAuthorities,
+ L"", // ScottBi - Why is this empty??
+ SidName.Buffer,
+ SidTypeAlias
+ )) {
+
+ goto InitializeWellKnownSidsError;
+ }
+
+ //
+ // Check if all Sids initialized.
+ //
+
+#ifdef LSAP_DEBUG_MESSAGE_STRINGS
+ DbgPrint("\nLSA (dbinit): Displaying all well known sids...\n\n");
+#endif //LSAP_DEBUG_MESSAGE_STRINGS
+
+ for (WellKnownSidIndex = LsapNullSidIndex;
+ WellKnownSidIndex < LsapDummyLastSidIndex;
+ WellKnownSidIndex++) {
+
+#ifdef LSAP_DEBUG_MESSAGE_STRINGS
+ DbgPrint(" *%wZ* : *%wZ*\n",
+ &OutputWellKnownSids[WellKnownSidIndex].DomainName,
+ &OutputWellKnownSids[WellKnownSidIndex].Name);
+#endif //LSAP_DEBUG_MESSAGE_STRINGS
+
+ if (OutputWellKnownSids[WellKnownSidIndex].Sid == NULL) {
+
+#if DBG
+ DbgPrint(
+ "Well Known Sid Index %d not initialized\n",
+ WellKnownSidIndex
+ );
+#endif //DBG
+
+ }
+ }
+
+ *WellKnownSids = OutputWellKnownSids;
+
+
+
+
+ return(TRUE);
+
+InitializeWellKnownSidsError:
+
+ return(FALSE);
+}
+
+
+BOOLEAN
+LsaIInitializeWellKnownSid(
+ OUT PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSids,
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex,
+ IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
+ IN UCHAR SubAuthorityCount,
+ IN PULONG SubAuthorities,
+ IN PWSTR Name,
+ IN PWSTR DomainName,
+ IN SID_NAME_USE Use
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes an entry in the specified well-known Sid table.
+ The entry contains the well known Sid and its name.
+
+Arguments:
+
+ WellKnownSids - Pointer to the first entry in the Well Known Sid table.
+
+ WellKnownSidIndex - Index into table of Well Known Sids.
+
+ Sid - Receives a pointer to a Sid with the correct size for the
+ number of subauthorities specified.
+
+ IdentifierAuthority - Pointer to Identifier authority.
+
+ SubAuthorityCount - Count of SubAuthorities
+
+ SubAuthorities - Array of SubAuthorities.
+
+ Name - Pointer to Unicode Name buffer containing the Sid's Name
+
+ DomainName - Pointer to Unicode Name buffer containing the
+ Sids Domain Name (if any) or descriptive text, such as
+ "Well Known Group" for Sids of Well Known Groups
+
+ SidNameUse - Specifies code for Sid's Use. The following values
+ may be specified:
+
+ SidTypeUser
+ SidTypeGroup
+ SidTypeDomain
+ SidTypeAlias
+ SidTypeWellKnownGroup
+ SidTypeDeletedAccount
+ SidTypeInvalid
+ SidTypeUnknown
+
+Return Value:
+
+ BOOLEAN - TRUE if Sid initialized, else FALSE.
+
+--*/
+
+{
+ PLSAP_WELL_KNOWN_SID_ENTRY
+ WellKnownSidEntry = &WellKnownSids[WellKnownSidIndex];
+
+ PSID OutputSid = NULL;
+
+ OutputSid = RtlAllocateHeap(
+ RtlProcessHeap(),
+ 0,
+ RtlLengthRequiredSid(SubAuthorityCount)
+ );
+
+ if (OutputSid == NULL) {
+
+ goto InitializeWellKnownSidError;
+ }
+
+ RtlInitializeSid( OutputSid, IdentifierAuthority, SubAuthorityCount);
+
+ if (SubAuthorityCount != 0) {
+
+ RtlCopyMemory(
+ RtlSubAuthoritySid( OutputSid, 0 ),
+ SubAuthorities,
+ SubAuthorityCount * sizeof(ULONG)
+ );
+ }
+
+ WellKnownSidEntry->Sid = OutputSid;
+
+ //
+ // Fill in the Domain Name
+ //
+
+ RtlInitUnicodeString(
+ &WellKnownSidEntry->DomainName,
+ DomainName
+ );
+
+ //
+ // Fill in the Use and Name.
+ //
+
+ WellKnownSidEntry->Use = Use;
+ RtlInitUnicodeString(
+ &WellKnownSidEntry->Name,
+ Name
+ );
+
+ return(TRUE);
+
+InitializeWellKnownSidError:
+
+#if DBG
+
+ DbgPrint("LSA Initialization of Well Known Sids Failed\n");
+ DbgPrint("Insufficient memory resources\n");
+
+#endif // DBG
+
+ return(FALSE);
+}
+
+
+NTSTATUS
+LsapGetMessageStrings(
+ LPVOID Resource,
+ DWORD Index1,
+ PUNICODE_STRING String1,
+ DWORD Index2,
+ PUNICODE_STRING String2 OPTIONAL
+ )
+
+
+/*++
+
+Routine Description:
+
+ This gets 1 or 2 message strings values from a resource message table.
+ The string buffers are allocated and the strings initialized properly.
+
+ The strings will be NULL terminated.
+
+ The string buffers must be freed using LocalFree() when no longer needed.
+
+Arguments:
+
+ Resource - points to the resource table.
+
+ Index1 - Index of first message to retrieve.
+
+ String1 - Points to a UNICODE_STRING structure to receive the first
+ message string. The string will be null terminated.
+
+ Index2 - Index of second message to retrieve.
+
+ String2 - Points to a UNICODE_STRING structure to receive the first
+ message string. If this parameter is NULL, then only one message
+ string is retrieved. The string will be null terminated.
+
+Return Value:
+
+ None.
+
+--*/
+
+
+{
+
+#ifdef LSAP_DEBUG_MESSAGE_STRINGS
+ DbgPrint("LSA (dbinit): String 1 -\n");
+ DbgPrint(" Index: 0x%lx\n", Index1);
+#endif //LSAP_DEBUG_MESSAGE_STRINGS
+
+
+ String1->Buffer = NULL;
+
+ String1->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ Resource,
+ Index1,
+ 0, // Use caller's language
+ (LPWSTR)&(String1->Buffer),
+ 0,
+ NULL
+ );
+
+ if (String1->Buffer == NULL) {
+ return(STATUS_RESOURCE_DATA_NOT_FOUND);
+ } else {
+
+ //
+ // Note that we are retrieving a message from a message file.
+ // This message will have a cr/lf tacked on the end of it
+ // (0x0d 0x0a) that we don't want to be part of our returned
+ // strings. However, we do need to null terminate our string
+ // so we will convert the 0x0d into a null terminator.
+ //
+ // Also note that FormatMessage() returns a character count,
+ // not a byte count. So, we have to do some adjusting to make
+ // the string lengths correct.
+ //
+
+ ASSERT(String1->MaximumLength >= 2); // We always expect cr/lf on our strings
+
+ //
+ // Adjust character count
+ //
+
+ String1->MaximumLength -= 1; // For the lf - we'll convert the cr.
+
+ //
+ // Set null terminator
+ //
+
+ String1->Buffer[String1->MaximumLength - 1] = 0;
+
+ //
+ // Change lengths to byte count instead of character count
+ //
+
+ String1->MaximumLength *= sizeof(WCHAR); // to make it a byte count
+ String1->Length = String1->MaximumLength - sizeof(WCHAR);
+
+#ifdef LSAP_DEBUG_MESSAGE_STRINGS
+ DbgPrint(" String: %wZ\n", String1);
+ DbgPrint(" Max: (0x%lx)\n", String1->MaximumLength);
+ DbgPrint(" Cur: (0x%lx)\n", String1->Length);
+ DbgPrint(" ");
+ {
+ ULONG i;
+ for (i=0; i<String1->MaximumLength; i++) {
+ DbgPrint("%2x ", (*((PUCHAR)String1->Buffer)+i));
+ }
+ DbgPrint("\n");
+ }
+#endif //LSAP_DEBUG_MESSAGE_STRINGS
+ }
+
+
+ if (!ARGUMENT_PRESENT(String2)) {
+ return(STATUS_SUCCESS);
+ }
+
+ String2->Buffer = NULL;
+ String2->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ Resource,
+ Index2,
+ 0, // Use caller's language
+ (LPWSTR)&(String2->Buffer),
+ 0,
+ NULL
+ );
+
+ if (String2->Buffer == NULL) {
+ LocalFree( String1->Buffer );
+ return(STATUS_RESOURCE_DATA_NOT_FOUND);
+ } else {
+
+ //
+ // Note that we are retrieving a message from a message file.
+ // This message will have a cr/lf tacked on the end of it
+ // (0x0d 0x0a) that we don't want to be part of our returned
+ // strings. However, we do need to null terminate our string
+ // so we will convert the 0x0d into a null terminator.
+ //
+ // Also note that FormatMessage() returns a character count,
+ // not a byte count. So, we have to do some adjusting to make
+ // the string lengths correct.
+ //
+
+ ASSERT(String2->MaximumLength >= 2); // We always expect cr/lf on our strings
+
+ //
+ // Adjust character count
+ //
+
+ String2->MaximumLength -= 1; // For the lf - we'll convert the cr.
+
+ //
+ // Set null terminator
+ //
+
+ String2->Buffer[String2->MaximumLength - 1] = 0;
+
+ //
+ // Change lengths to byte count instead of character count
+ //
+
+ String2->MaximumLength *= sizeof(WCHAR); // to make it a byte count
+ String2->Length = String2->MaximumLength - sizeof(WCHAR);
+
+#ifdef LSAP_DEBUG_MESSAGE_STRINGS
+ DbgPrint(" String: %wZ\n", String2);
+ DbgPrint(" Max: (0x%lx)\n", String2->MaximumLength);
+ DbgPrint(" Cur: (0x%lx)\n", String2->Length);
+ DbgPrint(" ");
+ {
+ ULONG i;
+ for (i=0; i<String2->MaximumLength; i++) {
+ DbgPrint("%2x ", (*((PUCHAR)String2->Buffer)+i));
+ }
+ DbgPrint("\n");
+ }
+#endif //LSAP_DEBUG_MESSAGE_STRINGS
+ }
+
+
+
+ return(STATUS_SUCCESS);
+
+}
diff --git a/private/lsa/server/dbinstac.c b/private/lsa/server/dbinstac.c
new file mode 100644
index 000000000..c0c893a75
--- /dev/null
+++ b/private/lsa/server/dbinstac.c
@@ -0,0 +1,643 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbinstac.c
+
+Abstract:
+
+ LSA Protected Subsystem - Account object Initialization.
+
+ This module sets up account objects to establish the default
+ Microsoft policy regarding privilege assignment, system access
+ rights (interactive, network, service), and abnormal quotas.
+
+Author:
+
+ Jim Kelly (JimK) May 3, 1992.
+
+Environment:
+
+ User mode - Does not depend on Windows.
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+
+NTSTATUS
+LsapDbInitializeAccount(
+ IN PSID AccountSid,
+ IN PLSAPR_PRIVILEGE_SET Privileges,
+ IN ULONG SystemAccess
+ );
+
+OLD_LARGE_INTEGER
+ConvertLongToOldLargeInteger(
+ ULONG u
+ )
+/*++
+
+Routine Description:
+
+ Coverts a long to old style large interger
+
+
+Arguments:
+
+ u - unsigned long.
+
+
+Return Value:
+
+ converted old style large integer.
+
+
+--*/
+{
+ LARGE_INTEGER NewLargeInteger;
+ OLD_LARGE_INTEGER OldLargeInteger;
+
+ NewLargeInteger = RtlConvertLongToLargeInteger(u);
+
+ NEW_TO_OLD_LARGE_INTEGER(
+ NewLargeInteger,
+ OldLargeInteger );
+
+ return( OldLargeInteger );
+
+}
+
+
+
+
+NTSTATUS
+LsapDbInstallAccountObjects(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function establishes ACCOUNT objects and initializes them
+ to contain the default Microsoft policy.
+
+Arguments:
+
+ None.
+
+
+Return Value:
+
+
+--*/
+
+{
+
+ NTSTATUS
+ Status = STATUS_SUCCESS;
+
+ ULONG
+ i,
+ Index,
+ SystemAccess;
+
+
+ SID_IDENTIFIER_AUTHORITY
+ WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY,
+ NtAuthority = SECURITY_NT_AUTHORITY;
+
+ PSID
+ WorldSid,
+ BuiltinAccountSid;
+
+ PLSAPR_PRIVILEGE_SET
+ Privileges;
+
+ UCHAR
+ PrivilegesBuffer[ sizeof(LSAPR_PRIVILEGE_SET) +
+ 20 * sizeof(LUID_AND_ATTRIBUTES)];
+
+
+
+
+
+
+ //
+ // Initialize our privilege set buffer
+ // (Room for 100 privileges)
+ //
+
+ Privileges = (PLSAPR_PRIVILEGE_SET)(&PrivilegesBuffer);
+ Privileges->Control = 0; //Not used here.
+ for (i=0; i<20; i++) {
+ Privileges->Privilege[i].Attributes = 0; //Disabled, DisabledByDefault
+ }
+
+
+
+ //
+ // Set up the SIDs we need.
+ // All builtin domain sids are the same length. We'll just create
+ // one and change its RID as necessary.
+ //
+
+
+ if (NT_SUCCESS(Status)) {
+ Status = RtlAllocateAndInitializeSid(
+ &WorldSidAuthority,
+ 1, //Sub authority count
+ SECURITY_WORLD_RID, //Sub authorities (up to 8)
+ 0, 0, 0, 0, 0, 0, 0,
+ &WorldSid
+ );
+ }
+
+ if (NT_SUCCESS(Status)) {
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &BuiltinAccountSid
+ );
+ }
+
+
+
+
+
+
+
+
+ //
+ // Now create each account and assign the appropriate set of privileges
+ // And logon capabilities. Some of these are product type-specific.
+ //
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // World account
+ // Logon types: Network
+ // Privileges:
+ // ChangeNotify (ENABLED)
+ //
+
+ Privileges->Privilege[0].Luid =
+ ConvertLongToOldLargeInteger(SE_CHANGE_NOTIFY_PRIVILEGE);
+ Privileges->Privilege[0].Attributes = SE_PRIVILEGE_ENABLED |
+ SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+
+ Privileges->PrivilegeCount = 1;
+
+ SystemAccess = SECURITY_ACCESS_NETWORK_LOGON;
+
+ //
+ // If a WinNt installation, give WORLD Interactive logon in
+ // and SHUTDOWN privilege in addition to Network Logon.
+ //
+
+ if (LsapProductType == NtProductWinNt) {
+
+ SystemAccess |= SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ Privileges->Privilege[1].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+ Privileges->Privilege[1].Attributes =
+ SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+ Privileges->PrivilegeCount = 2;
+ }
+
+ Status = LsapDbInitializeAccount(WorldSid, Privileges, SystemAccess);
+
+ Privileges->Privilege[0].Attributes = 0;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Admin Alias account
+ // Logon types: Interactive, Network
+ // Privileges:
+ // Security
+ // Backup
+ // Restore
+ // SetTime
+ // Shutdown
+ // RemoteShutdown
+ // Debug
+ // TakeOwnership
+ // SystemEnvironment
+ // SystemProfile
+ // SingleProcessProfile
+ // LoadDriver
+ // CreatePagefile
+ // IncreaseQuota
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON |
+ SECURITY_ACCESS_NETWORK_LOGON;
+ Index = 0;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_SECURITY_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_BACKUP_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_RESTORE_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_SYSTEMTIME_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_REMOTE_SHUTDOWN_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_TAKE_OWNERSHIP_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_DEBUG_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_SYSTEM_ENVIRONMENT_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_SYSTEM_PROFILE_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_PROF_SINGLE_PROCESS_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_INC_BASE_PRIORITY_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_LOAD_DRIVER_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_CREATE_PAGEFILE_PRIVILEGE);
+ Index++;
+
+ Privileges->Privilege[Index].Luid =
+ ConvertLongToOldLargeInteger(SE_INCREASE_QUOTA_PRIVILEGE);
+ Index++;
+
+ // to add another privilege, and add another group of lines ^^^
+
+ Privileges->PrivilegeCount = Index;
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_ADMINS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("LSA DB INSTALL: Creation of Administrators privileged account failed.\n"
+ " Status: 0x%lx\n", Status));
+ }
+
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Backup Operators Alias account
+ // Logon types: Interactive
+ // Privileges:
+ // Backup
+ // Restore
+ // Shutdown
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ Privileges->Privilege[0].Luid =
+ ConvertLongToOldLargeInteger(SE_BACKUP_PRIVILEGE);
+
+ Privileges->Privilege[1].Luid =
+ ConvertLongToOldLargeInteger(SE_RESTORE_PRIVILEGE);
+
+ Privileges->Privilege[2].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+
+ // to add another privilege, vvvv increment this, and add a line ^^^
+
+ Privileges->PrivilegeCount = 3;
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_BACKUP_OPS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+
+ }
+
+ if (LsapProductType == NtProductLanManNt) {
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // System Operators Alias account
+ // Logon types: Interactive
+ // Privileges:
+ // Backup
+ // Restore
+ // SetTime
+ // Shutdown
+ // RemoteShutdown
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ Privileges->Privilege[0].Luid =
+ ConvertLongToOldLargeInteger(SE_BACKUP_PRIVILEGE);
+
+ Privileges->Privilege[1].Luid =
+ ConvertLongToOldLargeInteger(SE_RESTORE_PRIVILEGE);
+
+ Privileges->Privilege[2].Luid =
+ ConvertLongToOldLargeInteger(SE_SYSTEMTIME_PRIVILEGE);
+
+ Privileges->Privilege[3].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+
+ Privileges->Privilege[4].Luid =
+ ConvertLongToOldLargeInteger(SE_REMOTE_SHUTDOWN_PRIVILEGE);
+
+ // to add another privilege, vvvv increment this, and add a line ^^^
+
+ Privileges->PrivilegeCount = 5;
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_SYSTEM_OPS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Account Operators Alias account
+ // Logon types: Interactive
+ // Privileges:
+ // Shutdown
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ Privileges->Privilege[0].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+
+ // to add another privilege, vvvv increment this, and add a line ^^^
+
+ Privileges->PrivilegeCount = 1;
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_ACCOUNT_OPS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Print Operators Alias account
+ // Logon types: Interactive
+ // Privileges:
+ // Shutdown
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ Privileges->Privilege[0].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+
+ // to add another privilege, vvvv increment this, and add a line ^^^
+
+ Privileges->PrivilegeCount = 1;
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_PRINT_OPS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+
+ }
+
+
+
+ } else {
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Power Users Alias account
+ // Logon types: Interactive, Network
+ // Privileges:
+ // Shutdown
+ // Set System Time
+ // SystemProfile
+ // SingleProcessProfile
+ // Debug (for developer installs ONLY!).
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON |
+ SECURITY_ACCESS_NETWORK_LOGON;
+
+ Privileges->Privilege[0].Luid =
+ ConvertLongToOldLargeInteger(SE_SYSTEMTIME_PRIVILEGE);
+
+ Privileges->Privilege[1].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+
+ Privileges->Privilege[2].Luid =
+ ConvertLongToOldLargeInteger(SE_REMOTE_SHUTDOWN_PRIVILEGE);
+
+ Privileges->Privilege[3].Luid =
+ ConvertLongToOldLargeInteger(SE_SYSTEM_PROFILE_PRIVILEGE);
+
+ Privileges->Privilege[3].Luid =
+ ConvertLongToOldLargeInteger(SE_PROF_SINGLE_PROCESS_PRIVILEGE);
+
+ Privileges->Privilege[4].Luid =
+ ConvertLongToOldLargeInteger(SE_INC_BASE_PRIORITY_PRIVILEGE);
+
+ // to add another privilege, vvvv increment this, and add a line ^^^
+
+ Privileges->PrivilegeCount = 5;
+
+
+ //
+ // Add privileges assigned for developer install
+ //
+ if (LsapSetupWasRun != TRUE) {
+
+ Privileges->Privilege[Privileges->PrivilegeCount].Luid =
+ ConvertLongToOldLargeInteger(SE_DEBUG_PRIVILEGE);
+ Privileges->PrivilegeCount++;
+ }
+
+
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_POWER_USERS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Users Alias account
+ // Logon types: Interactive
+ // Privileges:
+ // Shutdown
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ Privileges->Privilege[0].Luid =
+ ConvertLongToOldLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+
+ // to add another privilege, vvvv increment this, and add a line ^^^
+
+ Privileges->PrivilegeCount = 1;
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_USERS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+
+ }
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Guests Alias account
+ // Logon types: Interactive
+ // Privileges:
+ // None
+ //
+
+
+ SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ // to add another privilege, vvvv increment this, and add a line ^^^
+
+ Privileges->PrivilegeCount = 0;
+
+ (*RtlSubAuthoritySid(BuiltinAccountSid, 1)) = DOMAIN_ALIAS_RID_GUESTS;
+ Status = LsapDbInitializeAccount(BuiltinAccountSid, Privileges, SystemAccess);
+
+ }
+
+
+ }
+
+
+
+
+
+
+ //
+ // Free up SID buffers
+ //
+
+ RtlFreeSid( WorldSid );
+ RtlFreeSid( BuiltinAccountSid );
+
+
+
+
+ return(Status);
+
+
+}
+
+
+
+
+NTSTATUS
+LsapDbInitializeAccount(
+ IN PSID AccountSid,
+ IN PLSAPR_PRIVILEGE_SET Privileges,
+ IN ULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a single ACCOUNT object and assigns it the
+ privileges and system access specified.
+
+Arguments:
+
+ AccountSid - The SID of the account to create.
+
+ Privileges - The privileges, if any, to assign to the account.
+
+ SystemAccess - The logon capabilities, if any, to assign to the account.
+
+
+Return Value:
+
+
+--*/
+
+{
+
+ NTSTATUS
+ Status = STATUS_SUCCESS,
+ LocalStatus;
+
+ LSAPR_HANDLE
+ AccountHandle;
+
+
+ if ((Privileges->PrivilegeCount == 0) &&
+ (NT_SUCCESS(Status) && SystemAccess == 0) ) {
+ return(STATUS_SUCCESS);
+ }
+
+
+ Status = LsarCreateAccount( LsapDbHandle, AccountSid, 0, &AccountHandle);
+
+ if (NT_SUCCESS(Status)) {
+
+ if (Privileges->PrivilegeCount > 0) {
+ Status = LsarAddPrivilegesToAccount( AccountHandle, Privileges );
+ }
+
+ if (NT_SUCCESS(Status) && SystemAccess != 0) {
+ Status = LsarSetSystemAccessAccount( AccountHandle, SystemAccess);
+ }
+
+ LocalStatus = LsarClose( &AccountHandle );
+ ASSERT( NT_SUCCESS(LocalStatus) );
+ }
+
+ return(Status);
+
+}
diff --git a/private/lsa/server/dbinstal.c b/private/lsa/server/dbinstal.c
new file mode 100644
index 000000000..8692f0bf1
--- /dev/null
+++ b/private/lsa/server/dbinstal.c
@@ -0,0 +1,1335 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbinstal.c
+
+Abstract:
+
+ LSA Protected Subsystem - Database Installation.
+
+ This module contains code which will create an initial LSA Database
+ if none exists. Temporarily, this code is executed from within
+ LSA Initialization. This code will form part of the Security
+ Installation applet when implemented.
+
+ WARNING! THE CODE IN THIS MODULE IS TEMPORARY. IT WILL BE REPLACED
+ BY SYSTEM INSTALLATION FUNCTIONALITY.
+
+Author:
+
+ Scott Birrell (ScottBi) August 2, 1991
+
+Environment:
+
+ User mode - Does not depend on Windows.
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+
+
+
+
+VOID
+LsapDbSetDomainInfo(
+ IN PLSAP_DB_ATTRIBUTE *NextAttribute,
+ IN ULONG *AttributeCount
+ );
+
+
+NTSTATUS
+LsapDbGetNextValueToken(
+ IN PUNICODE_STRING Value,
+ IN OUT PULONG ParseContext,
+ OUT PUNICODE_STRING *ReturnString
+ );
+
+
+NTSTATUS
+LsapDbInstallLsaDatabase(
+ ULONG Pass
+ )
+
+/*++
+
+Routine Description:
+
+ This function installs an initial LSA Database. Any existing database
+ will be reset to have its initial attributes.
+
+Arguments:
+
+ Pass - Either 1 or 2. During pass 1 all information that is
+ not product type-specific is initialized. In pass 2,
+ the product type-specific stuff is initialized.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Install the LSA Database Policy Object.
+ //
+
+ Status = LsapDbInstallPolicyObject(Pass);
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbInstallPolicyObject(
+ IN ULONG Pass
+ )
+
+/*++
+
+Routine Description:
+
+ This function installs the LSA Database Policy Object, setting its attributes
+ to the default state. It is called as part of the LSA Database
+ Installation Procedure.
+
+Arguments:
+
+ Pass - Either 1 or 2. During pass 1 all information that is
+ not product type-specific is initialized. In pass 2,
+ the product type-specific stuff is initialized.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ LSAP_DB_HANDLE Handle;
+ POLICY_LSA_SERVER_ROLE ServerRole;
+ LSAP_DB_POLICY_PRIVATE_DATA PolicyPrivateData;
+ LSARM_POLICY_AUDIT_EVENTS_INFO InitialAuditEventInformation;
+ POLICY_AUDIT_LOG_INFO InitialAuditLogInformation;
+ LSAP_DB_ATTRIBUTE Attributes[20];
+ PLSAP_DB_ATTRIBUTE NextAttribute;
+ ULONG AttributeCount = 0;
+ ULONG InitialNextPosixOffset;
+ BOOLEAN ObjectReferenced = FALSE;
+ ULONG Revision;
+ NextAttribute = Attributes;
+
+ LsapDiagPrint( DB_INIT,
+ ("LSA (init): Performing pass %d of LSA Policy Initialization\n",
+ Pass ) );
+
+ if (Pass == 1) {
+
+ //
+ // Set up the Object Information for creating the Policy Object.
+ // Note that we put NULL for Security Quality Of Service since this
+ // open does not involve impersonation.
+ //
+
+ ObjectInformation.ObjectTypeId = PolicyObject;
+ ObjectInformation.Sid = NULL;
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ &LsapDbNames[Policy],
+ 0L,
+ NULL,
+ NULL
+ );
+
+ Handle = LsapDbHandle;
+
+
+
+ //
+ // Create the revision attribute
+ //
+
+ Revision = LSAP_DB_REVISION_1_1;
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolRevision],
+ &Revision,
+ sizeof (ULONG),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+
+
+ //
+ // Initialize the server role to Primary Domain Controller.
+ //
+
+ ServerRole = PolicyServerRolePrimary;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolSrvRo],
+ &ServerRole,
+ sizeof (POLICY_LSA_SERVER_ROLE),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Initialize the absolute minimum quota limit values for use in range
+ // checking done by LsaSetInformationPolicy() API. These values are
+ // hard-coded.
+ //
+
+ LsapDbAbsMinQuotaLimits.PagedPoolLimit = LSAP_DB_ABS_MIN_PAGED_POOL;
+ LsapDbAbsMinQuotaLimits.NonPagedPoolLimit = LSAP_DB_ABS_MIN_NON_PAGED_POOL;
+ LsapDbAbsMinQuotaLimits.MinimumWorkingSetSize =
+ LSAP_DB_ABS_MIN_MIN_WORKING_SET;
+ LsapDbAbsMinQuotaLimits.MaximumWorkingSetSize =
+ LSAP_DB_ABS_MIN_MAX_WORKING_SET;
+ LsapDbAbsMinQuotaLimits.PagefileLimit =
+ LSAP_DB_ABS_MIN_PAGEFILE;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[QuAbsMin],
+ &LsapDbAbsMinQuotaLimits,
+ sizeof (QUOTA_LIMITS),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Initialize the Abs Max Quota Limits.
+ //
+
+ LsapDbAbsMaxQuotaLimits.PagedPoolLimit = LSAP_DB_ABS_MAX_PAGED_POOL;
+ LsapDbAbsMaxQuotaLimits.NonPagedPoolLimit = LSAP_DB_ABS_MAX_NON_PAGED_POOL;
+ LsapDbAbsMaxQuotaLimits.MinimumWorkingSetSize =
+ LSAP_DB_ABS_MAX_MIN_WORKING_SET;
+ LsapDbAbsMaxQuotaLimits.MaximumWorkingSetSize =
+ LSAP_DB_ABS_MAX_MAX_WORKING_SET;
+ LsapDbAbsMaxQuotaLimits.PagefileLimit =
+ LSAP_DB_ABS_MAX_PAGEFILE;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[QuAbsMax],
+ &LsapDbAbsMaxQuotaLimits,
+ sizeof (QUOTA_LIMITS),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Install initial Private Data. For now, just one ULONG is stored
+ // so that the Replication Code can call LsaIGetPrivateData and
+ // LsaISetPrivateData successfully.
+ //
+
+ PolicyPrivateData.NoneDefinedYet = 0;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolState],
+ &PolicyPrivateData,
+ sizeof (LSAP_DB_POLICY_PRIVATE_DATA),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Initialize the Policy Modification Info. Set the Modification
+ // Id to 1 and set the Database Creation Time to the current time.
+ //
+
+ LsapDbState.PolicyModificationInfo.ModifiedId =
+ RtlConvertUlongToLargeInteger( (ULONG) 1 );
+
+ Status = NtQuerySystemTime(
+ &LsapDbState.PolicyModificationInfo.DatabaseCreationTime
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto InstallPolicyObjectError;
+ }
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolMod],
+ &LsapDbState.PolicyModificationInfo,
+ sizeof (POLICY_MODIFICATION_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Initialize the ModifiedIdAtLastPromotion Info.
+ //
+
+ LsapDbState.ModifiedIdAtLastPromotion =
+ LsapDbState.PolicyModificationInfo.ModifiedId;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolPromot],
+ &LsapDbState.ModifiedIdAtLastPromotion,
+ sizeof (LARGE_INTEGER),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Initialize Default Event Auditing Options. No auditing is specified
+ // for any event type. These will be set in the Policy Database later
+ // when the Policy Object is created.
+ //
+
+ Status = LsapAdtInitializeDefaultAuditing(
+ (ULONG) 0,
+ &InitialAuditEventInformation
+ );
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtEv],
+ &InitialAuditEventInformation,
+ sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ InitialNextPosixOffset = SE_INITIAL_TRUSTED_DOMAIN_POSIX_OFFSET;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolNxPxF],
+ &InitialNextPosixOffset,
+ sizeof (ULONG),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Create the Containing Directory "Accounts" for the user and group
+ // accounts objects.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[Accounts],
+ NULL,
+ 0L,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Create the Containing Directory "Domains" for the Trusted Domain
+ // objects.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[Domains],
+ NULL,
+ 0L,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Create the Containing Directory "Secrets" for the Secret objects.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[Secrets],
+ NULL,
+ 0L,
+ FALSE
+ );
+
+ //
+ // Create the Lsa Database Policy Object, opening the existing one if
+ // it exists.
+ //
+
+ NextAttribute++;
+ AttributeCount++;
+
+
+ //////////////////////////////////////////////////
+ // //
+ // ATTRIBUTES BELOW THIS POINT ARE INITIALIZED //
+ // IN PASS 1, BUT MAY BE CHANGED IN PASS 2. //
+ // IN GENERAL, THINGS ARE SET FOR A WIN-NT PROD //
+ // AND CHANGED IN PASS 2 IF NECESSARY. //
+ // //
+ //////////////////////////////////////////////////
+
+ //
+ // Initialize the default installed quota limit values
+ //
+
+ LsapDbInstalledQuotaLimits.PagedPoolLimit =
+ LSAP_DB_WINNT_PAGED_POOL;
+ LsapDbInstalledQuotaLimits.NonPagedPoolLimit =
+ LSAP_DB_WINNT_NON_PAGED_POOL;
+ LsapDbInstalledQuotaLimits.MinimumWorkingSetSize =
+ LSAP_DB_WINNT_MIN_WORKING_SET;
+ LsapDbInstalledQuotaLimits.MaximumWorkingSetSize =
+ LSAP_DB_WINNT_MAX_WORKING_SET;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[DefQuota],
+ &LsapDbInstalledQuotaLimits,
+ sizeof (QUOTA_LIMITS),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Initialize the audit log information.
+ //
+
+ InitialAuditLogInformation.MaximumLogSize = 8*1024;
+ InitialAuditLogInformation.AuditLogPercentFull = 0;
+ InitialAuditLogInformation.AuditRetentionPeriod.LowPart = 0x823543;
+ InitialAuditLogInformation.AuditRetentionPeriod.HighPart = 0;
+ InitialAuditLogInformation.AuditLogFullShutdownInProgress = FALSE;
+ InitialAuditLogInformation.TimeToShutdown.LowPart = 0x46656;
+ InitialAuditLogInformation.TimeToShutdown.HighPart = 0;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtLg],
+ &InitialAuditLogInformation,
+ sizeof (POLICY_AUDIT_LOG_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Set the default Audit Log Full Information. The Audit Log
+ // is not FULL and ShutDownOnFull is disabled.
+ //
+
+ LsapAdtLogFullInformation.LogIsFull = FALSE;
+ LsapAdtLogFullInformation.ShutDownOnFull = FALSE;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtFL],
+ &LsapAdtLogFullInformation,
+ sizeof (POLICY_AUDIT_FULL_QUERY_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ Status = LsapDbCreateObject(
+ &ObjectInformation,
+ GENERIC_ALL,
+ LSAP_DB_OBJECT_OPEN_IF,
+ LSAP_DB_TRUSTED,
+ Attributes,
+ AttributeCount,
+ &LsapDbHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbInstallPolicyObject: Create Policy object failed 0x%lx\n",
+ Status
+ );
+
+ LsapDiagPrint( DB_INIT,
+ ("LSA (init): Attributes passed to CreateObject call:\n\n"
+ " Count: %d\n"
+ " Array Address: 0x%lx",
+ AttributeCount, Attributes) );
+
+ ASSERT(NT_SUCCESS(Status)); // Provide a debug opportunity
+
+ goto InstallPolicyObjectError;
+ }
+
+ } else if (Pass == 2) {
+
+ //
+ // Set up the account objects necessary to implement the default
+ // Microsoft Policy for privilege assignment and system access
+ // capabilities.
+ //
+
+ Status = LsapDbInstallAccountObjects();
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LSA DB INSTALL: Installation of account objects failed.\n"
+ " Status: 0x%lx\n", Status));
+ goto InstallPolicyObjectError;
+ }
+
+ //
+ // Set up the account domain and primary domain information
+ // ONLY if the real setup wasn't run. In that case, we are
+ // doing a pseudo setup as part of a developer's first boot.
+ //
+
+ if (!LsapSetupWasRun) {
+
+ LsapDbSetDomainInfo( &NextAttribute, &AttributeCount );
+ }
+
+ if (LsapProductType == NtProductLanManNt) {
+
+ //
+ // quota limits were set for WinNt in pass 1.
+ // Change if necessary in this pass.
+ //
+
+ LsapDbInstalledQuotaLimits.PagedPoolLimit =
+ LSAP_DB_LANMANNT_PAGED_POOL;
+ LsapDbInstalledQuotaLimits.NonPagedPoolLimit =
+ LSAP_DB_LANMANNT_NON_PAGED_POOL;
+ LsapDbInstalledQuotaLimits.MinimumWorkingSetSize =
+ LSAP_DB_LANMANNT_MIN_WORKING_SET;
+ LsapDbInstalledQuotaLimits.MaximumWorkingSetSize =
+ LSAP_DB_LANMANNT_MAX_WORKING_SET;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[DefQuota],
+ &LsapDbInstalledQuotaLimits,
+ sizeof (QUOTA_LIMITS),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Audit log information was set for WinNt product
+ // in pass 1. Change if necessary in this pass.
+ //
+
+ InitialAuditLogInformation.MaximumLogSize = 20*1024;
+ InitialAuditLogInformation.AuditLogPercentFull = 0;
+ InitialAuditLogInformation.AuditRetentionPeriod.LowPart = 0x823543;
+ InitialAuditLogInformation.AuditRetentionPeriod.HighPart = 0;
+ InitialAuditLogInformation.AuditLogFullShutdownInProgress = FALSE;
+ InitialAuditLogInformation.TimeToShutdown.LowPart = 0x46656;
+ InitialAuditLogInformation.TimeToShutdown.HighPart = 0;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtLg],
+ &InitialAuditLogInformation,
+ sizeof (POLICY_AUDIT_LOG_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ }
+
+ if (AttributeCount > 0) {
+
+ Status = LsapDbReferenceObject(
+ LsapDbHandle,
+ 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapDiagPrint( DB_INIT,
+ ("LSA (init): Internal reference of Policy object failed.\n"
+ " Status of LsapDbReferenceObject == 0x%lx\n",
+ Status) );
+ goto InstallPolicyObjectError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ Status = LsapDbWriteAttributesObject(
+ LsapDbHandle,
+ Attributes,
+ AttributeCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapDiagPrint( DB_INIT,
+ ("LSA (init): Update of Policy attributes failed.\n"
+ " Attributes:\n\n"
+ " Count: %d\n"
+ " Array Address: 0x%lx",
+ AttributeCount, Attributes) );
+ goto InstallPolicyObjectError;
+ }
+
+ Status = LsapDbDereferenceObject(
+ &LsapDbHandle,
+ PolicyObject,
+ (LSAP_DB_RELEASE_LOCK |
+ LSAP_DB_FINISH_TRANSACTION),
+ (SECURITY_DB_DELTA_TYPE) 0,
+ STATUS_SUCCESS
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbInstallPolicyObject: Pass 2 DB init failed. 0x%lx\n",
+ Status
+ );
+
+ goto InstallPolicyObjectError;
+ }
+
+ ObjectReferenced = FALSE;
+ }
+ }
+
+InstallPolicyObjectFinish:
+
+ //
+ // If necessary, dereference the Policy Object.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &LsapDbHandle,
+ PolicyObject,
+ (LSAP_DB_RELEASE_LOCK |
+ LSAP_DB_FINISH_TRANSACTION),
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+InstallPolicyObjectError:
+
+ if (Pass == 1) {
+
+ LsapLogError(
+ "LsapDbInstallPolicyObject: Pass 1 DB init failed. 0x%lx\n",
+ Status
+ );
+
+ } else {
+
+ LsapLogError(
+ "LsapDbInstallPolicyObject: Pass 2 DB init failed. 0x%lx\n",
+ Status
+ );
+ }
+
+ goto InstallPolicyObjectFinish;
+}
+
+
+
+NTSTATUS
+LsapDbGetConfig (
+ IN HANDLE KeyHandle,
+ IN PWSTR Name,
+ OUT PUNICODE_STRING Value
+ )
+
+/*++
+
+Routine Description:
+
+ This routine obtains configuration information from the registry.
+
+Arguments:
+
+ KeyHandle - handle to registry key node containing value.
+
+ Name - The name of a value under the specifed key node.
+
+ Value - Fills in the string with the value of the parameter. The
+ returned string is zero terminated. The buffer is allocated in
+ Process Heap and should be deallocated by the caller.
+
+Return Value:
+
+ STATUS_SUCCESS - If the operation was successful.
+
+ STATUS_NO_MEMORY - There wasn't enough memory to allocate a buffer
+ to contain the returned information.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - The specifed section or the specified
+ keyword could not be found.
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING ValueName;
+ ULONG Length, ResultLength;
+ PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
+
+ RtlInitUnicodeString( &ValueName, Name );
+ Length = 512;
+ KeyValueInformation = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
+ if (KeyValueInformation == NULL) {
+ Status = STATUS_NO_MEMORY;
+ } else {
+ Status = NtQueryValueKey( KeyHandle,
+ &ValueName,
+ KeyValuePartialInformation,
+ KeyValueInformation,
+ Length,
+ &ResultLength
+ );
+ if (NT_SUCCESS( Status )) {
+ if (KeyValueInformation->Type != REG_SZ) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ }
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Value->MaximumLength = (USHORT)(KeyValueInformation->DataLength);
+ if (Value->MaximumLength >= sizeof(UNICODE_NULL)) {
+ Value->Length = (USHORT)KeyValueInformation->DataLength -
+ sizeof( UNICODE_NULL);
+ } else {
+ Value->Length = 0;
+ }
+ Value->Buffer = (PWSTR)KeyValueInformation;
+ RtlMoveMemory( Value->Buffer,
+ KeyValueInformation->Data,
+ Value->Length
+ );
+ Value->Buffer[ Value->Length / sizeof( WCHAR ) ] = UNICODE_NULL;
+ }
+#if DEVL
+ else {
+ DbgPrint( "LSA DB INSTALL: No '%wZ' value in registry - Status == %x\n", &ValueName, Status);
+ }
+#endif //DEVL
+
+ return Status;
+}
+
+
+
+NTSTATUS
+LsapDbGetNextValueToken(
+ IN PUNICODE_STRING Value,
+ IN OUT PULONG ParseContext,
+ OUT PUNICODE_STRING *ReturnString
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to isolate the next token in a registry value.
+
+ The token is returned in a single heap buffer containing both the
+ STRING and the Buffer of that string containing the token. The
+ caller of this routine is responsible for deallocating the buffer
+ when it is no longer needed.
+
+ The string, although counted, will also be null terminated.
+
+Arguments:
+
+ Value - Supplies the value line being parsed.
+
+ ParseContext - Is a pointer to a context state value.
+ The first time this routine is called for a particular
+ Value line, the value pointed to should be zero. Thereafter,
+ the value returned from the previous call should be passed.
+
+ ReturnString - Returns a pointer to the allocated string.
+
+
+Return Value:
+
+ STATUS_SUCCESS - indicates the next token has been isolated.
+
+ STATUS_INVALID_PARAMTER_1 - Indicates there were no more tokens in
+ the Value line.
+
+ STATUS_NO_MEMORY - memory could not be allocated for the token.
+
+--*/
+
+{
+ ULONG i, j;
+ ULONG TokenLength;
+ ULONG AllocSize;
+
+
+ //
+ // Get to the beginning of the next token
+ //
+
+ for ( i = *ParseContext;
+ i < (Value->Length/sizeof(WCHAR)) &&
+ (Value->Buffer[i] == L' ' || Value->Buffer[i] == L'\t');
+ i++ )
+ ;
+
+
+ //
+ // see if we ran off the end of the string..
+ //
+
+ if (i >= (Value->Length/sizeof(WCHAR))) {
+ return STATUS_INVALID_PARAMETER_1;
+ }
+
+
+ //
+ // Now search for the end of the token
+ //
+
+ for ( j = i + 1;
+ j < (Value->Length/sizeof(WCHAR)) &&
+ Value->Buffer[j] != L' ' && Value->Buffer[j] != L'\t';
+ j++ )
+ ;
+
+ *ParseContext = j;
+
+ //
+ // We've either reached the end of the string, or found the end of the
+ // token.
+ //
+
+ //
+ // If the caller actually wants the string returned,
+ // allocate and copy it.
+ //
+
+ if ( ARGUMENT_PRESENT( ReturnString ) ) {
+ UNICODE_STRING SourceString;
+ PUNICODE_STRING LocalString;
+
+ TokenLength = (j-i) * sizeof(WCHAR);
+ AllocSize = sizeof(UNICODE_STRING) + (TokenLength + sizeof( UNICODE_NULL ) + 4);
+
+ LocalString = RtlAllocateHeap( RtlProcessHeap(), 0, AllocSize );
+ if ( LocalString == NULL ) {
+ DbgPrint("LSA DB INSTALL: LsapDbGetNextValueToken: Not enough memory %ld\n",
+ AllocSize);
+ return STATUS_NO_MEMORY;
+ }
+ LocalString->MaximumLength = (USHORT)(TokenLength + sizeof( UNICODE_NULL ));
+ LocalString->Length = (USHORT)TokenLength;
+ LocalString->Buffer = (PWCHAR)(LocalString + 1);
+
+
+ //
+ // Now copy the token
+ //
+
+ SourceString.MaximumLength = LocalString->Length;
+ SourceString.Length = LocalString->Length;
+ SourceString.Buffer = &Value->Buffer[i];
+
+ RtlCopyUnicodeString( LocalString, &SourceString );
+
+ //
+ // Add a null terminator
+ //
+
+ LocalString->Buffer[LocalString->Length / sizeof( UNICODE_NULL )] = UNICODE_NULL;
+ *ReturnString = LocalString;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+
+
+VOID
+LsapDbSetDomainInfo(
+ IN PLSAP_DB_ATTRIBUTE *NextAttribute,
+ IN ULONG *AttributeCount
+ )
+
+/*
+
+ This routine is only used for the pseudo setup for internal
+ developer's use. In a real product installation/setup
+ situation, The functionality performed by this routine is
+ performed by the text-mode setup supplemented by the network
+ setup.
+
+ This routine must establish values for the AccountDomain and
+ PrimaryDomain attributes of the Policy object. These
+ attributes must be configured as follows:
+
+
+ I. Standalone Win-NT product
+
+ AccountDomainName = "Account"
+ AccountDomainSid = (value assigned by user)
+ PrimaryDomainName = Name of domain to use for browsing
+ (this is optional in this case)
+ PrimaryDomainSid = (None)
+
+
+ II. Non-Standalone Win-NT product
+
+ AccountDomainName = "Account"
+ AccountDomainSid = (value assigned by user)
+ PrimaryDomainName = (Primary domain's name)
+ PrimaryDomainSid = (Primary domain's SID)
+
+
+ III. LanMan-NT product
+
+ AccountDomainName = (Primary domain's name)
+ AccountDomainSid = (Primary domain's SID)
+ PrimaryDomainName = (Primary domain's name)
+ PrimaryDomainSid = (Primary domain's SID)
+
+
+ This routine only does (II) and (III). The real setup must
+ be capable of doing (I) as well.
+
+*/
+
+{
+ NTSTATUS Status;
+ NT_PRODUCT_TYPE ProductType;
+ BOOLEAN ProductExplicitlySpecified;
+
+ UNICODE_STRING PrimaryDomainName, AccountDomainName;
+ PSID PrimaryDomainSid, AccountDomainSid;
+
+ HANDLE KeyHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING KeyName;
+ ULONG TempULong;
+
+ //
+ // Get the product type
+ //
+
+ ProductExplicitlySpecified =
+ RtlGetNtProductType( &ProductType );
+
+#if DBG
+if (ProductType == NtProductLanManNt) {
+ DbgPrint("LSA DB INSTALL: Configuring LSA database for LanManNt system.\n");
+} else {
+ DbgPrint("LSA DB INSTALL: Configuring LSA database for WinNt or Dedicated Server product.\n");
+}
+#endif //DBG
+
+ //
+ // Open a handle to the registry key node that contains the
+ // interesting domain values (name, id, and account id)
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters" );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+ Status = NtOpenKey( &KeyHandle, KEY_READ, &ObjectAttributes );
+ if (!NT_SUCCESS( Status )) {
+#if DEVL
+ DbgPrint( "LSA DB INSTALL: Unable to access registry key (%wZ) - Status == %x\n", &KeyName, Status );
+#endif // DBG
+ return;
+ }
+
+ //
+ // Get the primary domain name from the registry
+ //
+
+ Status = LsapDbGetConfig(KeyHandle,
+ L"Domain",
+ &PrimaryDomainName);
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NtClose( KeyHandle );
+ return;
+ }
+
+ //
+ // get the primary domain's SID
+ //
+
+
+ {
+ SID_IDENTIFIER_AUTHORITY TmppAuthority;
+ UNICODE_STRING DomainId;
+ ULONG DomainSubAuthorities[SID_MAX_SUB_AUTHORITIES];
+ UCHAR DomainSubAuthorityCount = 0;
+
+ ULONG i;
+ ULONG Context = 0;
+ PUNICODE_STRING Rid;
+ ULONG Size;
+
+ Status = LsapDbGetConfig(KeyHandle,
+ L"DomainId",
+ &DomainId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NtClose( KeyHandle );
+ return;
+ }
+
+
+ //
+ // Get the Authority ID from the registry
+ //
+
+ for (i=0; i<sizeof(TmppAuthority.Value)/sizeof(TmppAuthority.Value[0]); i++ ) {
+
+ Status = LsapDbGetNextValueToken( &DomainId, &Context, &Rid );
+ if (NT_SUCCESS( Status )) {
+ Status = RtlUnicodeStringToInteger(Rid, 10, &TempULong );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint("LSA DB INSTALL: domainid - must have at least %ld subauthorities\n",
+ sizeof(TmppAuthority.Value)/sizeof(TmppAuthority.Value[0]));
+#endif //DBG
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ NtClose( KeyHandle );
+ return;
+ }
+
+
+ TmppAuthority.Value[i] = (UCHAR)TempULong;
+ RtlFreeHeap( RtlProcessHeap(), 0, Rid );
+ }
+
+ //
+ // Get some subauthorities from the registry
+ //
+
+ for (i=0; ; i++ ) {
+
+ Status = LsapDbGetNextValueToken( &DomainId, &Context, &Rid );
+ if (NT_SUCCESS( Status )) {
+ Status = RtlUnicodeStringToInteger(Rid, 10, &TempULong );
+ }
+
+ if ( Status == STATUS_INVALID_PARAMETER_1 ) {
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ break;
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ NtClose( KeyHandle );
+ return;
+ }
+
+ if ( i >=
+ sizeof(DomainSubAuthorities)/sizeof(DomainSubAuthorities[0]) ){
+#if DBG
+ DbgPrint("LSA DB INSTALL: domainid - "
+ "Too many Domain subauthorities specified (%ld maximum).\n",
+ sizeof(DomainSubAuthorities)/sizeof(DomainSubAuthorities[0]));
+#endif //DBG
+
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ NtClose( KeyHandle );
+ return;
+ }
+
+ DomainSubAuthorities[i] = TempULong;
+ DomainSubAuthorityCount ++;
+ RtlFreeHeap( RtlProcessHeap(), 0, Rid );
+
+ }
+
+ //
+ // Allocate memory to put the domain id in.
+ //
+
+ Size = RtlLengthRequiredSid( DomainSubAuthorityCount );
+
+ PrimaryDomainSid = RtlAllocateHeap( RtlProcessHeap(), 0, Size );
+
+ ASSERT( PrimaryDomainSid != NULL );
+
+ RtlInitializeSid( PrimaryDomainSid,
+ &TmppAuthority,
+ DomainSubAuthorityCount );
+
+ for ( i=0; i < (ULONG) DomainSubAuthorityCount; i++ ) {
+ *(RtlSubAuthoritySid(PrimaryDomainSid, i)) =
+ DomainSubAuthorities[i];
+ }
+
+ }
+
+
+
+
+
+
+ if (ProductType != NtProductLanManNt) {
+
+
+ SID_IDENTIFIER_AUTHORITY TmppAuthority;
+ UNICODE_STRING DomainId;
+ ULONG DomainSubAuthorities[SID_MAX_SUB_AUTHORITIES];
+ UCHAR DomainSubAuthorityCount = 0;
+
+ ULONG i;
+ ULONG Context = 0;
+ PUNICODE_STRING Rid;
+ ULONG Size;
+
+
+
+
+ //
+ // if the system is a WinNt product, then get the account domain
+ // SID from the registry info and set a well known name ("ACCOUNT").
+ //
+
+
+
+ RtlInitUnicodeString(&AccountDomainName,L"Account");
+ Status = LsapDbGetConfig(KeyHandle,
+ L"AccountDomainId",
+ &DomainId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NtClose( KeyHandle );
+ return;
+ }
+
+
+ //
+ // Get the Authority ID from the registry
+ //
+
+ for (i=0; i<sizeof(TmppAuthority.Value)/sizeof(TmppAuthority.Value[0]); i++ ) {
+
+ Status = LsapDbGetNextValueToken( &DomainId, &Context, &Rid );
+ if (NT_SUCCESS( Status )) {
+ Status = RtlUnicodeStringToInteger(Rid, 10, &TempULong );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint("LSA DB INSTALL: AccountDomainId - must have at least %ld subauthorities\n",
+ sizeof(TmppAuthority.Value)/sizeof(TmppAuthority.Value[0]));
+#endif //DBG
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ NtClose( KeyHandle );
+ return;
+ }
+
+ TmppAuthority.Value[i] = (UCHAR)TempULong;
+ RtlFreeHeap( RtlProcessHeap(), 0, Rid );
+ }
+
+ //
+ // Get some subauthorities from the registry
+ //
+
+ for (i=0; ; i++ ) {
+
+ Status = LsapDbGetNextValueToken( &DomainId, &Context, &Rid );
+ if (NT_SUCCESS( Status )) {
+ Status = RtlUnicodeStringToInteger(Rid, 10, &TempULong );
+ }
+
+ if ( Status == STATUS_INVALID_PARAMETER_1 ) {
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ break;
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ NtClose( KeyHandle );
+ return;
+ }
+
+ if ( i >=
+ sizeof(DomainSubAuthorities)/sizeof(DomainSubAuthorities[0]) ){
+#if DBG
+ DbgPrint("MsV1_0: NT.CFG: domainid - Too many Domain subauthorities specified (%ld maximum).\n",
+ sizeof(DomainSubAuthorities)/sizeof(DomainSubAuthorities[0]));
+#endif //DBG
+
+ RtlFreeHeap( RtlProcessHeap(), 0, DomainId.Buffer );
+ NtClose( KeyHandle );
+ return;
+ }
+
+ DomainSubAuthorities[i] = TempULong;
+ DomainSubAuthorityCount ++;
+ RtlFreeHeap( RtlProcessHeap(), 0, Rid );
+
+ }
+
+ //
+ // Allocate memory to put the domain id in.
+ //
+
+ Size = RtlLengthRequiredSid( DomainSubAuthorityCount );
+
+ AccountDomainSid = RtlAllocateHeap( RtlProcessHeap(), 0, Size );
+
+ ASSERT( AccountDomainSid != NULL );
+
+ RtlInitializeSid( AccountDomainSid,
+ &TmppAuthority,
+ DomainSubAuthorityCount );
+
+ for ( i=0; i < (ULONG) DomainSubAuthorityCount; i++ ) {
+ *(RtlSubAuthoritySid(AccountDomainSid, i)) =
+ DomainSubAuthorities[i];
+ }
+
+ } else {
+
+ //
+ // Otherwise, the account domain is set up just like the
+ // primary domain
+ //
+
+ AccountDomainName = PrimaryDomainName;
+
+ AccountDomainSid = PrimaryDomainSid;
+ }
+
+ //
+ // Close the registry key handle, as we are done with it.
+ //
+
+ NtClose( KeyHandle );
+
+
+ //
+ // Now add the attributes to be initialized in the policy object...
+ //
+
+
+
+ //
+ // Primary domain name/sid
+ //
+
+
+ Status = LsapDbMakeUnicodeAttribute(
+ &PrimaryDomainName,
+ &LsapDbNames[PolPrDmN],
+ (*NextAttribute)
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ (*NextAttribute)++;
+ (*AttributeCount)++;
+
+
+
+ Status = LsapDbMakeSidAttribute(
+ PrimaryDomainSid,
+ &LsapDbNames[PolPrDmS],
+ (*NextAttribute)
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ (*NextAttribute)++;
+ (*AttributeCount)++;
+
+
+ //
+ // Account domain name/sid
+ //
+
+ Status = LsapDbMakeUnicodeAttribute(
+ &AccountDomainName,
+ &LsapDbNames[PolAcDmN],
+ (*NextAttribute)
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ (*NextAttribute)++;
+ (*AttributeCount)++;
+
+
+
+ Status = LsapDbMakeSidAttribute(
+ AccountDomainSid,
+ &LsapDbNames[PolAcDmS],
+ (*NextAttribute)
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ (*NextAttribute)++;
+ (*AttributeCount)++;
+
+
+
+
+}
diff --git a/private/lsa/server/dblookup.c b/private/lsa/server/dblookup.c
new file mode 100644
index 000000000..70cc0dbf8
--- /dev/null
+++ b/private/lsa/server/dblookup.c
@@ -0,0 +1,13138 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ dblookup.c
+
+Abstract:
+
+ LSA Database - Lookup Sid and Name routines
+
+ NOTE: This module should remain as portable code that is independent
+ of the implementation of the LSA Database. As such, it is
+ permitted to use only the exported LSA Database interfaces
+ contained in db.h and NOT the private implementation
+ dependent functions in dbp.h.
+
+Author:
+
+ Scott Birrell (ScottBi) November 27, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "ausrvp.h"
+#include "dbp.h"
+
+//////////////////////////////////////////////////////////////////////////
+// //
+// Lsa Lookup Sid and Name Private Global State Variables // //
+// //
+//////////////////////////////////////////////////////////////////////////
+
+LARGE_INTEGER LsapDbLookupTimeout;
+HANDLE LsapDbLookupCompleteEvent = NULL;
+HANDLE LsapDbLookupStartedEvent = NULL;
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// //
+// Lsa Lookup Sid and Name Routines //
+// //
+//////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsarLookupSids(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID_ENUM_BUFFER SidEnumBuffer,
+ OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the LSA Server worker routine for the LsaLookupSids
+ API.
+
+ The LsaLookupSids API attempts to find names corresponding to Sids.
+ If a name can not be mapped to a Sid, the Sid is converted to character
+ form. The caller must have POLICY_LOOKUP_NAMES access to the Policy
+ object.
+
+ WARNING: This routine allocates memory for its output. The caller is
+ responsible for freeing this memory after use. See description of the
+ Names parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ SidEnumBuffer - Pointer to an enumeration buffer containing a count
+ and a pointer to an array of Count pointers to Sids to be mapped
+ to names. The Sids may be well_known SIDs, SIDs of User accounts
+ Group Accounts, Alias accounts, or Domains.
+
+ ReferencedDomains - Receives a pointer to a structure describing the
+ domains used for the translation. The entries in this structure
+ are referenced by the structure returned via the Names parameter.
+ Unlike the Names parameter, which contains an array entry
+ for (each translated name, this strutcure will only contain
+ component for each domain utilized in the translation.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ TranslatedNames - Pointer to a structure which will reference an array
+ records describing each translated name. The nth entry in this array
+ provides a translation for the nth entry in the Sids parameter.
+
+ All of the returned names will be isolated names or NULL strings
+ (domain names are returned as NULL strings). If the caller needs
+ composite names, they can be generated by prepending the
+ isolated name with the domain name and a backslash. For example,
+ if (the name Sally is returned, and it is from the domain Manufact,
+ then the composite name would be "Manufact" + "\" + "Sally" or
+ "Manufact\Sally".
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ If a Sid is not translatable, then the following will occur:
+
+ 1) If the SID's domain is known, then a reference domain record
+ will be generated with the domain's name. In this case, the
+ name returned via the Names parameter is a Unicode representation
+ of the relative ID of the account, such as "(3cmd14)" or the null
+ string, if the Sid is that of a domain. So, you might end up
+ with a resultant name of "Manufact\(314) for the example with
+ Sally above, if Sally's relative id is 314.
+
+ 2) If not even the SID's domain could be located, then a full
+ Unicode representation of the SID is generated and no domain
+ record is referenced. In this case, the returned string might
+ be something like: "(S-1-672194-21-314)".
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupWksta - First Level Lookup performed on a workstation
+ normally configured for Windows-Nt. The lookup searches the
+ Well-Known Sids/Names, and the Built-in Domain and Account Domain
+ in the local SAM Database. If not all Sids or Names are
+ identified, performs a "handoff" of a Second level Lookup to the
+ LSA running on a Controller for the workstation's Primary Domain
+ (if any).
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids or Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ MappedCount - Pointer to location that contains a count of the Sids
+ mapped so far. On exit, this will be updated.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully and all Sids have
+ been translated to names.
+
+ STATUS_SOME_NOT_MAPPED - At least one of the Sids provided was
+ translated to a Name, but not all Sids could be translated. This
+ is a success status.
+
+ STATUS_NONE_MAPPED - None of the Sids provided could be translated
+ to names. This is an error status, but output is returned. Such
+ output includes partial translations of Sids whose domain could
+ be identified, together with their Relative Id in Unicode format,
+ and character representations of Sids whose domain could not
+ be identified.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_DOMAIN_CTRLR_CONFIG_ERROR - Target machine not configured
+ as a DC when expected.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory to complete the call.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ PLSAPR_SID *Sids = (PLSAPR_SID *) SidEnumBuffer->SidInfo;
+ ULONG Count = SidEnumBuffer->Entries;
+ BOOLEAN PolicyHandleReferencedHere = FALSE;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ PPOLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo = NULL;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
+ LSA_HANDLE ControllerPolicyHandle = NULL;
+ ULONG DomainIndex;
+ ULONG SidIndex;
+ LSAPR_TRUST_INFORMATION TrustInformation;
+ PLSAPR_TRANSLATED_NAME OutputNames = NULL;
+ ULONG OutputNamesLength;
+ PLSAPR_TRUST_INFORMATION Domains = NULL;
+ ULONG CompletelyUnmappedCount = Count;
+ ULONG LocalDomainsToSearch = 0;
+ BOOLEAN AlreadyTranslated = FALSE;
+ LUID LogonId;
+
+
+
+ SecondaryStatus = STATUS_SUCCESS;
+
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_LOOKUP_NAMES,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // We can dereference the original PolicyHandle and release the lock on
+ // the LSA Database: if we need to access the database again, we'll
+ // use the trusted Lsa handle and the appropriate API will take
+ // the lock as required.
+ //
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+
+
+ TranslatedNames->Entries = SidEnumBuffer->Entries;
+ TranslatedNames->Names = NULL;
+ *ReferencedDomains = NULL;
+
+
+ //
+ // Verify that all of the Sids passed in are syntactically valid.
+ //
+
+ for (SidIndex = 0; SidIndex < Count; SidIndex++) {
+
+ if ((Sids[SidIndex] != NULL) && RtlValidSid( (PSID) Sids[SidIndex])) {
+
+ continue;
+ }
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Allocate Output Names array buffer. For now don't place its address on
+ // the Free List as this buffer contains others that will be placed on
+ // that list and the order of freeing is unknown.
+ //
+
+ OutputNamesLength = Count * sizeof(LSA_TRANSLATED_NAME);
+ OutputNames = MIDL_user_allocate(OutputNamesLength);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (OutputNames == NULL) {
+
+ goto LookupSidsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ TranslatedNames->Entries = SidEnumBuffer->Entries;
+ TranslatedNames->Names = OutputNames;
+
+
+ //
+ // Initialize Output Names array, marking Sid Use as unknown and
+ // specifying negative DomainIndex.
+ //
+
+ RtlZeroMemory( OutputNames, OutputNamesLength);
+
+ for (SidIndex = 0; SidIndex < Count; SidIndex++) {
+
+ OutputNames[SidIndex].Use = SidTypeUnknown;
+ OutputNames[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
+ }
+
+ //
+ // Create an empty Referenced Domain List.
+ //
+
+ Status = LsapDbLookupCreateListReferencedDomains( ReferencedDomains, 0 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+
+ if ( Count == 1 ) {
+
+ PUNICODE_STRING AccountName;
+ PUNICODE_STRING AuthorityName;
+ PSID UserSid;
+ PSID DomainSid = NULL;
+ ULONG Rid;
+ PLSAP_LOGON_SESSION LogonSession;
+ PTOKEN_USER TokenUserInformation;
+
+ //
+ // Let's see if we're trying to look up the currently logged on
+ // user.
+ //
+ //
+ // TokenUserInformation from this call must be freed by calling
+ // LsapFreeLsaHeap().
+ //
+
+ Status = LsapQueryClientInfo(
+ &TokenUserInformation,
+ &LogonId
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ goto NormalLookupPath;
+ }
+
+ if ( RtlEqualSid( TokenUserInformation->User.Sid, Sids[0] )) {
+
+ LsapFreeLsaHeap( TokenUserInformation );
+
+ //
+ // Got a match. Get the username and domain information
+ // from the LogonId
+ //
+
+ LsapAuLock();
+
+ LogonSession = LsapGetLogonSession ( &LogonId, FALSE );
+
+ //
+ // During setup, we may get NULL returned for the logon session.
+ //
+
+ if (LogonSession == NULL) {
+
+ LsapAuUnlock();
+ goto NormalLookupPath;
+ }
+
+ UserSid = LogonSession->UserSid;
+ AccountName = LogonSession->AccountName;
+ AuthorityName = LogonSession->AuthorityName;
+
+ //
+ // DomainSid will be allocated for us, free with MIDL_user_free
+ //
+
+ Status = LsapSplitSid( UserSid, &DomainSid, &Rid );
+
+ if ( !NT_SUCCESS(Status)) {
+ LsapAuUnlock();
+ goto NormalLookupPath;
+ }
+
+ RtlCopyMemory(
+ &TrustInformation.Name,
+ AuthorityName,
+ sizeof( UNICODE_STRING )
+ );
+
+ TrustInformation.Sid = DomainSid;
+
+ //
+ // Fill in the Output Translated Name structure. Note that the
+ // buffers for the SID and Unicode Name must be allocated via
+ // MIDL_user_allocate() since they will be freed by the calling
+ // RPC server stub routine lsarpc_LsarLookupSids() after marshalling.
+ //
+
+ OutputNames[0].Use = SidTypeUser;
+ OutputNames[0].DomainIndex = 0;
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ (PUNICODE_STRING) &OutputNames[0].Name,
+ AccountName
+ );
+
+ //
+ // Username, AccountName, and UserSid have all been copied
+ // from the LogonSession structure, so we can release the AuLock now.
+ //
+
+ LsapAuUnlock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ MIDL_user_free(DomainSid);
+ goto LookupSidsError;
+ }
+
+ //
+ // Make an entry in the list of Referenced Domains.
+ //
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ *ReferencedDomains,
+ &TrustInformation,
+ (PLONG) &OutputNames[0].DomainIndex
+ );
+
+ ASSERT( OutputNames[0].DomainIndex == 0 );
+
+ //
+ // DomainSid has been copied, free it now
+ //
+
+ MIDL_user_free( DomainSid );
+
+ if (!NT_SUCCESS(Status)) {
+ goto NormalLookupPath;
+ }
+
+ *MappedCount = 1;
+
+ return( STATUS_SUCCESS );
+
+ } else {
+
+ LsapFreeLsaHeap( TokenUserInformation );
+ }
+ }
+
+NormalLookupPath:
+
+ //
+ // The local domains to be searched always include the Accounts
+ // domain. For initial lookup targets only, the BUILT_IN domain is
+ // also searched.
+ //
+
+ LocalDomainsToSearch = LSAP_DB_SEARCH_ACCOUNT_DOMAIN;
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ LocalDomainsToSearch |= LSAP_DB_SEARCH_BUILT_IN_DOMAIN;
+
+ //
+ // This is the lowest Lookup Level, normally targeted at a
+ // Workstation but possibly targeted at a DC. Make a first pass of
+ // the array of Sids to identify any well-known Isolated Sids. These
+ // are Well Known Sids that do not belong to a real domain.
+ //
+
+ Status = LsapDbLookupIsolatedWellKnownSids(
+ Count,
+ Sids,
+ *ReferencedDomains,
+ TranslatedNames,
+ MappedCount,
+ &CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // If all Sids are now mapped or partially mapped, finish.
+ //
+
+ if (CompletelyUnmappedCount == (ULONG) 0) {
+ goto LookupSidsFinish;
+ }
+ }
+
+ //
+ // There are some remaining completely unmapped Sids. They may belong to
+ // a local SAM Domain. Currently, there are two such domains, the
+ // Built-in Domain and the Accounts Domain. For initial Lookup Level
+ // we search both of these domains. For higher Lookup Levels we search
+ // only the Accounts domain.
+ //
+
+ Status = LsapDbLookupSidsInLocalDomains(
+ Count,
+ Sids,
+ *ReferencedDomains,
+ TranslatedNames,
+ MappedCount,
+ &CompletelyUnmappedCount,
+ LocalDomainsToSearch
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto LookupSidsError;
+ }
+
+ //
+ // If all Sids are now mapped or partially mapped, finish.
+ //
+
+ if (CompletelyUnmappedCount == (ULONG) 0) {
+ goto LookupSidsFinish;
+ }
+
+ //
+ // Not all of the Sids have been identified in the local domains(s).
+ // The next step in the search depends on the level of this lookup
+ // and how we are configured as follows:
+ //
+ // Lookup Level Configuration Lookup search next
+ //
+ // LsapLookupWksta Win Nt Primary Domain
+ // LanMan Nt Trusted Domains
+ //
+ // LsapLookupPDC Win Nt error
+ // LanMan Nt Trusted Domains
+ //
+ // LsaLookupTDL Win Nt error
+ // LanMan Nt none
+ //
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ if (LsapProductType != NtProductLanManNt) {
+
+ Status = LsarQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // If there is no Primary Domain as in the case of a WORKGROUP,
+ // just finish up. Set a default result code STATUS_SUCCESS. This
+ // will be translated to STATUS_SOME_NOT_MAPPED or STATUS_NONE_MAPPED
+ // as appropriate.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ if (PolicyPrimaryDomainInfo->Sid == NULL) {
+ goto LookupSidsFinish;
+ }
+
+ //
+ // There is a Primary Domain. Search it for Sids. Since a
+ // Primary Domain is also a Trusted Domain, we use the
+ // Trusted Domain search routine. This routine will "hand off"
+ // the search to a Domain Controller's LSA.
+ //
+
+ RtlCopyMemory(
+ &TrustInformation.Name,
+ &PolicyPrimaryDomainInfo->Name,
+ sizeof( UNICODE_STRING)
+ );
+
+ TrustInformation.Sid = (PSID) PolicyPrimaryDomainInfo->Sid;
+
+ Status = LsapDbLookupSidsInPrimaryDomain(
+ Count,
+ Sids,
+ &TrustInformation,
+ *ReferencedDomains,
+ TranslatedNames,
+ LsapLookupPDC,
+ MappedCount,
+ &CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+
+ goto LookupSidsFinish;
+ }
+ }
+
+ //
+ // We reach here in two cases:
+ //
+ // * Initial Level lookups targeted at DC's
+ // * Higher Level Lookups (must be targeted at DC's)
+ //
+ // For the highest level lookup, that on an individual TDC, there
+ // is no more searching to do, since we have already searched the
+ // Accounts Domain and we do not follow trust relationships on DC's
+ // beyond one level.
+ //
+
+ if (LookupLevel == LsapLookupTDL) {
+
+ goto LookupSidsFinish;
+ }
+
+ //
+ // We are either the initial target of the lookup but not configured
+ // as a workstation, or we are the target of a Primary Domain
+ // level lookup. In either case, we must be configured as a DC.
+ //
+
+ Status = STATUS_DOMAIN_CTRLR_CONFIG_ERROR;
+
+ if (LsapProductType != NtProductLanManNt) {
+
+ goto LookupSidsError;
+ }
+
+ //
+ // Obtain the Trusted Domain List and search all Trusted Domains
+ // except ourselves.
+ //
+
+ Status = LsapDbLookupSidsInTrustedDomains(
+ Count,
+ Sids,
+ (PLSAPR_REFERENCED_DOMAIN_LIST) *ReferencedDomains,
+ TranslatedNames,
+ LsapLookupTDL,
+ MappedCount,
+ &CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+
+LookupSidsFinish:
+
+ //
+ // If there are any unknown Sids (including partially mapped Sids)
+ // we need to translate them to character form. We do this translation
+ // at the lowest lookup level in all non-error cases and also in the
+ // error case where none were mapped.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ if ((*MappedCount < Count) &&
+ (LookupLevel == LsapLookupWksta) &&
+ (AlreadyTranslated == FALSE)) {
+
+ AlreadyTranslated = TRUE;
+
+ //
+ // The remaining unmapped Sids are unknown. They are either
+ // completely unmapped, i.e. their domain is unknown, or
+ // partially unmapped, their domain being known but their Rid
+ // not being recognized. For completely unmapped Sids, translate
+ // the entire Sid to character form. For partially unmapped
+ // Sids, translate the Relative Id only to character form.
+ //
+
+ Status = LsapDbLookupTranslateUnknownSids(
+ Count,
+ Sids,
+ *ReferencedDomains,
+ TranslatedNames,
+ *MappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsError;
+ }
+ }
+ }
+
+ //
+ // If some but not all Sids were mapped, return informational status
+ // STATUS_SOME_NOT_MAPPED. If no Sids were mapped, return error
+ // STATUS_NONE_MAPPED.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ if (*MappedCount < Count) {
+
+ Status = STATUS_SOME_NOT_MAPPED;
+
+ if (*MappedCount == 0) {
+
+ Status = STATUS_NONE_MAPPED;
+ }
+ }
+ }
+
+ //
+ // If necessary, free the Policy Primary Domain Info
+ //
+
+ if (PolicyPrimaryDomainInfo != NULL) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyPrimaryDomainInformation,
+ (PLSAPR_POLICY_INFORMATION) PolicyPrimaryDomainInfo
+ );
+
+ PolicyPrimaryDomainInfo = NULL;
+ }
+
+//
+// Temporary debug aid. Trying to find a recurring problem on MIPS
+// free builds on printlab6 running spool stress. This should access
+// violate if the problem has been encountered.
+//
+{ // Temporary debug
+ ULONG TmpDiag;
+ if (TranslatedNames->Names != NULL) {
+ TmpDiag = (TranslatedNames->Names[0].DomainIndex);
+ }
+} // end temporary debug
+
+ return(Status);
+
+LookupSidsError:
+
+ //
+ // If the LookupLevel is the lowest (Workstation Level, free up
+ // the Names and Referenced Domains arrays.
+ //
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ //
+ // If necessary, free the Names array.
+ //
+
+ if (TranslatedNames->Names != NULL) {
+
+ OutputNames = TranslatedNames->Names;
+
+ for (SidIndex = 0; SidIndex < Count; SidIndex++ ) {
+
+ if (OutputNames[SidIndex].Name.Buffer != NULL) {
+
+ MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
+ OutputNames[SidIndex].Name.Buffer = NULL;
+ }
+ }
+
+ MIDL_user_free( TranslatedNames->Names );
+ TranslatedNames->Names = NULL;
+ }
+
+ //
+ // If necessary, free the Referenced Domain List.
+ //
+
+ if (*ReferencedDomains != NULL) {
+
+ Domains = (*ReferencedDomains)->Domains;
+
+ if (Domains != NULL) {
+
+ for (DomainIndex = 0;
+ DomainIndex < (*ReferencedDomains)->Entries;
+ DomainIndex++) {
+
+ if (Domains[ DomainIndex ].Name.Buffer != NULL) {
+
+ MIDL_user_free( Domains[ DomainIndex ].Name.Buffer );
+ Domains[ DomainIndex ].Name.Buffer == NULL;
+ }
+
+ if (Domains[ DomainIndex ].Sid != NULL) {
+
+ MIDL_user_free( Domains[ DomainIndex ].Sid );
+ Domains[ DomainIndex ].Sid == NULL;
+ }
+ }
+ }
+
+ MIDL_user_free( *ReferencedDomains );
+ *ReferencedDomains = NULL;
+ }
+ }
+
+ //
+ // If the primary status was a success code, but the secondary
+ // status was an error, propagate the secondary status.
+ //
+
+ if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto LookupSidsFinish;
+}
+
+
+NTSTATUS
+LsapDbEnumerateSids(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function enumerates Sids of objects of a given type within a container
+ object. Since there may be more information than can be returned in a
+ single call of the routine, multiple calls can be made to get all of the
+ information. To support this feature, the caller is provided with a
+ handle that can be used across calls. On the initial call,
+ EnumerationContext should point to a variable that has been initialized
+ to 0.
+
+Arguments:
+
+ ContainerHandle - Handle to a container object.
+
+ ObjectTypeId - Type of object to be enumerated. The type must be one
+ for which all objects have Sids.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ DbEnumerationBuffer - Receives a pointer to a structure that will receive
+ the count of entries returned in an enumeration information array, and
+ a pointer to the array. Currently, the only information returned is
+ the object Sids. These Sids may be used together with object type to
+ open the objects and obtain any further information available.
+
+ PreferedMaximumLength - prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+ CountReturned - Pointer to variable which will receive a count of the
+ entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if no objects have been enumerated because the
+ EnumerationContext value passed in is too high.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_ENUMERATION_ELEMENT LastElement;
+ PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement, FreeElement;
+ ULONG DataLengthUsed;
+ ULONG ThisBufferLength;
+ PSID *Sids = NULL;
+ BOOLEAN PreferedMaximumReached = FALSE;
+ ULONG EntriesRead;
+ ULONG Index, EnumerationIndex;
+ BOOLEAN TrustedClient = ((LSAP_DB_HANDLE) ContainerHandle)->Trusted;
+
+ LastElement.Next = NULL;
+ FirstElement = &LastElement;
+
+ //
+ // If no enumeration buffer provided, return an error.
+ //
+
+
+ if ( !ARGUMENT_PRESENT(DbEnumerationBuffer) ||
+ !ARGUMENT_PRESENT(EnumerationContext ) ) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+ //
+ // Enumerate objects, stopping when the length of data to be returned
+ // reaches or exceeds the Prefered Maximum Length, or reaches the
+ // absolute maximum allowed for LSA object enumerations. We allow
+ // the last object enumerated to bring the total amount of data to
+ // be returned beyond the Prefered Maximum Length, but not beyond the
+ // absolute maximum length.
+ //
+
+ EnumerationIndex = *EnumerationContext;
+
+ for(DataLengthUsed = 0, EntriesRead = 0;
+ DataLengthUsed < PreferedMaximumLength;
+ DataLengthUsed += ThisBufferLength, EntriesRead++) {
+
+ //
+ // If the absolute maximum length has been exceeded, back off
+ // the last object enumerated.
+ //
+
+ if ((DataLengthUsed > LSA_MAXIMUM_ENUMERATION_LENGTH) &&
+ (!TrustedClient)) {
+
+ FirstElement = NextElement->Next;
+ MIDL_user_free(NextElement);
+ break;
+ }
+
+ //
+ // Allocate memory for next enumeration element. Set the Sid
+ // field to NULL for cleanup purposes.
+ //
+
+ NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));
+
+ if (NextElement == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ //
+ // Find the next object's Sid, and fill in its object information.
+ // Note that memory will be allocated via MIDL_user_allocate
+ // and must be freed when no longer required.
+ //
+
+ Status = LsapDbFindNextSid(
+ ContainerHandle,
+ &EnumerationIndex,
+ ObjectTypeId,
+ (PLSAPR_SID *) &NextElement->Sid
+ );
+
+ //
+ // Stop the enumeration if any error or warning occurs. Note
+ // that the warning STATUS_NO_MORE_ENTRIES will be returned when
+ // we've gone beyond the last index.
+ //
+
+ if (Status != STATUS_SUCCESS) {
+
+ //
+ // Since NextElement is not on the list, it will not get
+ // freed at the end so we must free it here.
+ //
+
+ MIDL_user_free( NextElement );
+ break;
+ }
+
+ //
+ // Get the length of the data allocated for the object's Sid
+ //
+
+ ThisBufferLength = RtlLengthSid( NextElement->Sid );
+
+ //
+ // Link the object just found to the front of the enumeration list
+ //
+
+ NextElement->Next = FirstElement;
+ FirstElement = NextElement;
+ }
+
+ //
+ // If an error other than STATUS_NO_MORE_ENTRIES occurred, return it.
+ // If STATUS_NO_MORE_ENTRIES was returned, we have enumerated all of the
+ // entries. In this case, return STATUS_SUCCESS if we enumerated at
+ // least one entry, otherwise propagate STATUS_NO_MORE_ENTRIES back to
+ // the caller.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ goto EnumerateSidsError;
+ }
+
+ if (EntriesRead == 0) {
+
+ goto EnumerateSidsError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Some entries were read, allocate an information buffer for returning
+ // them.
+ //
+
+ Sids = (PSID *) MIDL_user_allocate( sizeof (PSID) * EntriesRead );
+
+ if (Sids == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto EnumerateSidsError;
+ }
+
+ //
+ // Memory was successfully allocated for the return buffer.
+ // Copy in the enumerated Sids.
+ //
+
+ for (NextElement = FirstElement, Index = 0;
+ NextElement != &LastElement;
+ NextElement = NextElement->Next, Index++) {
+
+ ASSERT(Index < EntriesRead);
+
+ Sids[Index] = NextElement->Sid;
+ }
+
+EnumerateSidsFinish:
+
+ //
+ // Free the enumeration element structures (if any).
+ //
+
+ for (NextElement = FirstElement; NextElement != &LastElement;) {
+
+ //
+ // If an error has occurred, dispose of memory allocated
+ // for any Sids.
+ //
+
+ if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {
+
+ if (NextElement->Sid != NULL) {
+
+ MIDL_user_free(NextElement->Sid);
+ }
+ }
+
+ //
+ // Free the memory allocated for the enumeration element.
+ //
+
+ FreeElement = NextElement;
+ NextElement = NextElement->Next;
+
+ MIDL_user_free(FreeElement);
+ }
+
+ //
+ // Fill in return enumeration structure (0 and NULL in error case).
+ //
+
+ DbEnumerationBuffer->EntriesRead = EntriesRead;
+ DbEnumerationBuffer->Sids = Sids;
+ *EnumerationContext = EnumerationIndex;
+
+ return(Status);
+
+EnumerateSidsError:
+
+ //
+ // If necessary, free memory allocated for returning the Sids.
+ //
+
+ if (Sids != NULL) {
+
+ MIDL_user_free( Sids );
+ Sids = NULL;
+ }
+
+ goto EnumerateSidsFinish;
+}
+
+
+NTSTATUS
+LsapDbFindNextSid(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ OUT PLSAPR_SID *NextSid
+ )
+
+/*++
+
+Routine Description:
+
+ This function finds the next Sid of object of a given type within a
+ container object. The given object type must be one where objects
+ have Sids. The Sids returned can be used on subsequent open calls to
+ access the objects.
+
+Arguments:
+
+ ContainerHandle - Handle to container object.
+
+ EnumerationContext - Pointer to a variable containing the index of
+ the object to be found. A zero value indicates that the first
+ object is to be found.
+
+ ObjectTypeId - Type of the objects whose Sids are being enumerated.
+
+ NextSid - Receives a pointer to the next Sid found.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - Invalid ContainerHandle specified
+
+ STATUS_NO_MORE_ENTRIES - Warning that no more entries exist.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ ULONG SidKeyValueLength = 0;
+ UNICODE_STRING SubKeyNameU;
+ UNICODE_STRING SidKeyNameU;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ContDirKeyHandle = NULL;
+ HANDLE SidKeyHandle = NULL;
+ PSID ObjectSid = NULL;
+
+ //
+ // Zeroise pointers for cleanup routine
+ //
+
+ SidKeyNameU.Buffer = NULL;
+ SubKeyNameU.Buffer = NULL;
+
+ //
+ // Setup object attributes for opening the appropriate Containing
+ // Directory. For example, if we're looking for Account objects,
+ // the containing Directory is "Accounts". The Unicode strings for
+ // containing Directories are set up during Lsa Initialization.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &LsapDbContDirs[ObjectTypeId],
+ OBJ_CASE_INSENSITIVE,
+ ((LSAP_DB_HANDLE) ContainerHandle)->KeyHandle,
+ NULL
+ );
+
+ Status = RtlpNtOpenKey(
+ &ContDirKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ ContDirKeyHandle = NULL; // For error processing
+ goto FindNextError;
+ }
+
+ //
+ // Initialize the Unicode String in which the next object's Logical Name
+ // will be returned. The Logical Name of an object equals its Registry
+ // Key relative to its Containing Directory, and is also equal to
+ // the Relative Id of the object represented in character form as an
+ // 8-digit number with leading zeros.
+ //
+ // NOTE: The size of buffer allocated for the Logical Name must be
+ // calculated dynamically when the Registry supports long names, because
+ // it is possible that the Logical Name of an object will be equal to a
+ // character representation of the full Sid, not just the Relative Id
+ // part.
+ //
+
+ SubKeyNameU.MaximumLength = (USHORT) LSAP_DB_LOGICAL_NAME_MAX_LENGTH;
+ SubKeyNameU.Length = 0;
+ SubKeyNameU.Buffer = LsapAllocateLsaHeap(SubKeyNameU.MaximumLength);
+
+ if (SubKeyNameU.Buffer == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto FindNextError;
+ }
+
+ //
+ // Now enumerate the next subkey.
+ //
+
+ Status = RtlpNtEnumerateSubKey(
+ ContDirKeyHandle,
+ &SubKeyNameU,
+ *EnumerationContext,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ //
+ // Construct a path to the Sid attribute of the object relative to
+ // the containing directory. This path has the form
+ //
+ // <Object Logical Name>"\Sid"
+ //
+ // The Logical Name of the object has just been returned by the
+ // above call to RtlpNtEnumerateSubKey.
+ //
+
+ Status = LsapDbJoinSubPaths(
+ &SubKeyNameU,
+ &LsapDbNames[Sid],
+ &SidKeyNameU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ //
+ // Setup object attributes for opening the Sid attribute
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &SidKeyNameU,
+ OBJ_CASE_INSENSITIVE,
+ ContDirKeyHandle,
+ NULL
+ );
+
+ //
+ // Open the Sid attribute
+ //
+
+ Status = RtlpNtOpenKey(
+ &SidKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ SidKeyHandle = NULL;
+ goto FindNextError;
+ }
+
+ //
+ // Now query the size of the buffer required to read the Sid
+ // attribute's value.
+ //
+
+ SidKeyValueLength = 0;
+
+ Status = RtlpNtQueryValueKey(
+ SidKeyHandle,
+ NULL,
+ NULL,
+ &SidKeyValueLength,
+ NULL
+ );
+
+ //
+ // We expect buffer overflow to be returned from a query buffer size
+ // call.
+ //
+
+ if (Status == STATUS_BUFFER_OVERFLOW) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ goto FindNextError;
+ }
+
+ //
+ // Allocate memory for reading the Sid attribute.
+ //
+
+ ObjectSid = MIDL_user_allocate(SidKeyValueLength);
+
+ if (ObjectSid == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto FindNextError;
+ }
+
+ //
+ // Supplied buffer is large enough to hold the SubKey's value.
+ // Query the value.
+ //
+
+ Status = RtlpNtQueryValueKey(
+ SidKeyHandle,
+ NULL,
+ ObjectSid,
+ &SidKeyValueLength,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ (*EnumerationContext)++;
+
+ //
+ // Return the Sid.
+ //
+
+ *NextSid = ObjectSid;
+
+FindNextFinish:
+
+ //
+ // If necessary, close the Sid key handle
+ //
+
+ if (SidKeyHandle != NULL) {
+
+ SecondaryStatus = NtClose(SidKeyHandle);
+
+#if DBG
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ DbgPrint("LsapDbFindNextSid: NtClose failed 0x%lx\n", Status);
+ }
+
+#endif // DBG
+
+ }
+
+ //
+ // If necessary, close the containing directory handle
+ //
+
+ if (ContDirKeyHandle != NULL) {
+
+ SecondaryStatus = NtClose(ContDirKeyHandle);
+
+#if DBG
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ DbgPrint(
+ "LsapDbFindNextSid: NtClose failed 0x%lx\n",
+ Status
+ );
+ }
+
+#endif // DBG
+
+ }
+
+ //
+ // If necessary, free the Unicode String buffer allocated by
+ // LsapDbJoinSubPaths for the Registry key name of the Sid attribute
+ // relative to the containing directory Registry key.
+ //
+
+ if (SidKeyNameU.Buffer != NULL) {
+
+ RtlFreeUnicodeString( &SidKeyNameU );
+ }
+
+ //
+ // If necessary, free the Unicode String buffer allocated for
+ // Registry key name of the object relative to its containing
+ // directory.
+ //
+
+ if (SubKeyNameU.Buffer != NULL) {
+
+ LsapFreeLsaHeap( SubKeyNameU.Buffer );
+ }
+
+ return(Status);
+
+FindNextError:
+
+ //
+ // If necessary, free the memory allocated for the object's Sid.
+ //
+
+ if (ObjectSid != NULL) {
+
+ MIDL_user_free(ObjectSid);
+ *NextSid = NULL;
+ }
+
+ goto FindNextFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupIsolatedWellKnownSids(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to identify Sids as Isolated Well-Known Sids
+ (Well-Known Sids that do not belong to a domain) and translate them to
+ names. Note that Domain Sids for the Well Known domains themselves
+ (e.g the Sid of the Built-in Domain) will be identified.
+
+ WARNING: This function allocates memory for translated names. The
+ caller is responsible for freeing this memory after it is no longer
+ required.
+
+Arguments:
+
+ Count - Specifies the count of Sids provided in the array Sids.
+
+ Sids - Pointer to an array of Sids to be examined.
+
+ TranslatedNames - Pointer to structure that will be initialized to
+ references an array of Name translations for the Sids.
+
+ ReferencedDomains - Pointer to a structure that will be initialized to
+ reference a list of the domains used for the translation.
+
+ The entries in this structure are referenced by the
+ structure returned via the Names parameter. Unlike the Names
+ parameter, which contains an array entry for (each translated name,
+ this structure will only contain one component for each domain
+ utilized in the translation.
+
+ If the specified location contains NULL, a structure will be allocated
+ via MIDL_user_allocate.
+
+ MappedCount - Pointer to location that contains on entry, the number
+ of Sids in Sids that have been translated so far. This number
+ is updated if any further Sids are translated by this call.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Sids. A Sid is completely unmapped
+ if it is unknown and also its Domain Prefix Sid is not recognized.
+ This count is updated on exit, the number of completely unmapped
+ Sids whose Domain Prefices are identified by this routine being
+ subtracted from the input value.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Sids may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory to complete the call.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter or parameter combination.
+ - *MappedCount > Count
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG SidNumber;
+ ULONG UnmappedSidsRemaining;
+ PLSAPR_TRANSLATED_NAME OutputNames = NULL;
+ ULONG PrefixSidLength;
+ UCHAR SubAuthorityCount;
+ LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
+ LSAPR_TRUST_INFORMATION TrustInformation;
+ PLSAPR_SID Sid = NULL;
+ PLSAPR_SID PrefixSid = NULL;
+ LSAP_MM_FREE_LIST FreeList;
+ ULONG CleanupFreeListOptions = (ULONG) 0;
+
+ OutputNames = TranslatedNames->Names;
+
+ UnmappedSidsRemaining = Count;
+
+ //
+ // Initialize the Free List. We need potentially one entry per Sid,
+ // for its Well Known Name (if any).
+ //
+
+ Status = LsapMmCreateFreeList(&FreeList, UnmappedSidsRemaining);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupIsolatedWellKnownSidsError;
+ }
+
+ //
+ // Attempt to identify Sids as Well Known Isolated Sids
+ //
+
+ for (SidNumber = 0; SidNumber < Count; SidNumber++) {
+
+ Sid = Sids[SidNumber];
+
+ //
+ // Attempt to identify the next Sid using the Well Known Sids table,
+ // excluding those Sids that are also in the Built In domain. For
+ // those, we drop through to the Built in Domain search. Note
+ // that only one of these, the Administrators alias is currently
+ // present in the table.
+ //
+
+ if (LsapDbLookupIndexWellKnownSid( Sid, &WellKnownSidIndex ) &&
+ (WellKnownSidIndex != LsapAliasAdminsSidIndex)) {
+
+ //
+ // Sid is identified. Copy its Well Known Name field
+ // UNICODE_STRING structure. Note that not all Well Known
+ // Sids have Well Known Names. For those Sids without a
+ // Well Known Name, this UNICODE_STRING structure specifies
+ // the NULL string.
+ //
+
+ Status = LsapRpcCopyUnicodeString(
+ &FreeList,
+ (PUNICODE_STRING) &(OutputNames[SidNumber].Name),
+ LsapDbWellKnownSidName(WellKnownSidIndex)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Get the Sid's Use.
+ //
+
+ OutputNames[SidNumber].Use = LsapDbWellKnownSidNameUse(WellKnownSidIndex);
+
+ PrefixSid = NULL;
+
+ //
+ // If the Sid is a Domain Sid, store pointer to
+ // it in the Trust Information.
+ //
+
+ if (OutputNames[SidNumber].Use == SidTypeDomain) {
+
+ TrustInformation.Sid = Sid;
+
+ } else {
+
+ //
+ // The Sid is not a domain Sid. Construct the
+ // Prefix Sid. This is equal to the original Sid
+ // excluding the lowest subauthority (Relative Id).
+ //
+
+ SubAuthorityCount = *RtlSubAuthorityCountSid((PSID) Sid);
+
+ PrefixSidLength = RtlLengthRequiredSid(SubAuthorityCount - 1);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ PrefixSid = MIDL_user_allocate( PrefixSidLength );
+
+ if (PrefixSid == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ RtlCopyMemory( PrefixSid, Sid, PrefixSidLength );
+
+ (*RtlSubAuthorityCountSid( (PSID) PrefixSid ))--;
+
+ TrustInformation.Sid = PrefixSid;
+ }
+
+ //
+ // Lookup this Domain Sid or Prefix Sid in the Referenced Domain
+ // List. If it is already there, return the DomainIndex for the
+ // existing entry and free up the memory allocated for the
+ // Prefix Sid (if any).
+ //
+
+ if (LsapDbLookupListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation.Sid,
+ (PLONG) &(OutputNames[SidNumber].DomainIndex)
+ )) {
+
+ if ((OutputNames[SidNumber].Use == SidTypeDomain) ||
+ (OutputNames[SidNumber].Name.Buffer != NULL)) {
+
+ UnmappedSidsRemaining--;
+ }
+
+ if (PrefixSid != NULL) {
+
+ MIDL_user_free(PrefixSid);
+ PrefixSid = NULL;
+ }
+
+ continue;
+ }
+
+ //
+ // This Domain or Prefix Sid is not currently on the
+ // Referenced Domain List. Complete a Trust Information
+ // entry and add it to the List. Copy in the Domain Name
+ // (Domain Sids) or NULL string. Note that we use
+ // RtlCopyMemory to copy a UNICODE_STRING structure onto
+ // a LSAPR_UNICODE_STRING structure.
+ //
+
+ RtlCopyMemory(
+ &TrustInformation.Name,
+ &WellKnownSids[WellKnownSidIndex].DomainName,
+ sizeof(UNICODE_STRING)
+ );
+
+ //
+ // If the Sid has been recognized as a Well Known Sid and
+ // is either a Domain Sid or has a well-known name, count
+ // it as being mapped and add the Built-in Domain to the
+ // Referenced Domain List.
+ //
+
+ if ((OutputNames[SidNumber].Use == SidTypeDomain) ||
+ (OutputNames[SidNumber].Name.Length != 0)) {
+
+ UnmappedSidsRemaining--;
+
+ //
+ // Make an entry in the list of Referenced Domains. Note
+ // that in the case of well-known Sids, the Prefix Sid
+ // may or may not be the Sid of a Domain. For those well
+ // known Sids whose prefix Sid is not a domain Sid, the
+ // Name field in the Trust Information has been set to the
+ // NULL string.
+ //
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ &TrustInformation,
+ (PLONG) &OutputNames[SidNumber].DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ } else {
+
+ //
+ // The Sid is recognized as a Well Known Sid, but is
+ // not a Domain Sid and does not have a Well Known Name
+ // (signified by a zero length name string). Filter this
+ // out.
+ //
+
+ OutputNames[SidNumber].Use = SidTypeUnknown;
+ OutputNames[SidNumber].Name.Length = (USHORT) 0;
+ OutputNames[SidNumber].Name.MaximumLength = (USHORT) 0;
+ OutputNames[SidNumber].Name.Buffer = NULL;
+ }
+
+ //
+ // If memory was allocated for a Prefix Sid, free it. Note that
+ // the LsapDbLookupAddListTrustedDomains routine will have made
+ // a copy of the Sid.
+ //
+
+ if (PrefixSid != NULL) {
+
+ MIDL_user_free(PrefixSid);
+ PrefixSid = NULL;
+ }
+ }
+ }
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto LookupIsolatedWellKnownSidsError;
+ }
+
+LookupIsolatedWellKnownSidsFinish:
+
+ //
+ // If there is a final PrefixSid buffer, free it.
+ //
+
+ if (PrefixSid != NULL) {
+
+ MIDL_user_free(PrefixSid);
+ PrefixSid = NULL;
+ }
+
+ //
+ // Delete the Free List, freeing buffers on the list if an error
+ // occurred.
+ //
+
+ LsapMmCleanupFreeList( &FreeList, CleanupFreeListOptions );
+
+ //
+ // Return output parameters.
+ //
+
+ *MappedCount = Count - UnmappedSidsRemaining;
+ *CompletelyUnmappedCount = UnmappedSidsRemaining;
+ return(Status);
+
+LookupIsolatedWellKnownSidsError:
+
+ //
+ // Specify that buffers on Free List are to be freed.
+ //
+
+ CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
+ goto LookupIsolatedWellKnownSidsFinish;
+}
+
+
+BOOLEAN
+LsapDbLookupIndexWellKnownSid(
+ IN PLSAPR_SID Sid,
+ OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up a Sid to determine if it is well-known. If so,
+ an index into the table of well-known Sids is returned.
+
+Arguments:
+
+ Sid - Pointer to Sid to be looked up.
+
+ WellKnownSidIndex - Pointer to variable that will receive the
+ index of the Sid if well known.
+
+Return Value:
+
+ BOOLEAN - TRUE if the Sid is well-known, else FALSE
+
+--*/
+
+{
+ LSAP_WELL_KNOWN_SID_INDEX Index;
+
+ //
+ // Scan the table of well-known Sids looking for a match.
+ //
+
+ for(Index = LsapWorldSidIndex; Index<LsapDummyLastSidIndex; Index++) {
+
+ //
+ // Allow NULL entries in the table of well-known Sids for now.
+ //
+
+ if (WellKnownSids[Index].Sid == NULL) {
+
+ continue;
+ }
+
+ //
+ // If a match is found, return the index to the caller.
+ //
+
+ if (RtlEqualSid((PSID) Sid, WellKnownSids[Index].Sid)) {
+
+ *WellKnownSidIndex = Index;
+ return TRUE;
+ }
+ }
+
+ //
+ // The Sid is not a well-known Sid. Return FALSE.
+ //
+
+ return FALSE;
+}
+
+
+ULONG LsapDbGetSizeTextSid(
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This function computes the size of ASCIIZ buffer required for a
+ Sid in character form. Temporarily, the size returned is an over-
+ estimate, because 9 digits are allowed for the decimal equivalent
+ of each 32-bit SubAuthority value.
+
+Arguments:
+
+ Sid - Pointer to Sid to be sized
+
+Return Value:
+
+ ULONG - The required size of buffer is returned.
+
+--*/
+
+{
+ ULONG TextSidSize = 0;
+
+ //
+ // Count the Sid prefix and revision "S-rev-". The revision is
+ // assumed to not exceed 2 digits.
+ //
+
+ TextSidSize = sizeof("S-nn-");
+
+ //
+ // Add in the size of the identifier authority
+ //
+
+ TextSidSize += 15; // log base 10 of 48 (= 6-byte number)
+
+ //
+ // If the Sid has SubAuthorities, count 9 bytes for each one
+ //
+
+ if (((PLSAPR_SID) Sid)->SubAuthorityCount > 0) {
+
+ TextSidSize += (ULONG)
+ (9 * ((PLSAPR_SID) Sid)->SubAuthorityCount);
+ }
+
+ return TextSidSize;
+}
+
+
+NTSTATUS
+LsapDbSidToTextSid(
+ IN PSID Sid,
+ OUT PSZ TextSid
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a Sid to character text and places it in the
+ supplied buffer. The buffer is assumed to be of sufficient size, as
+ can be computed by calling LsapDbGetSizeTextSid().
+
+Arguments:
+
+ Sid - Pointer to Sid to be converted.
+
+ TextSid - Optional pointer to the buffer in which the converted
+ Sid will be placed as an ASCIIZ. A NULL pointer ma
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ PLSAPR_SID ISid = Sid;
+ ULONG Index;
+ ULONG IdentifierAuthorityValue;
+ UCHAR Buffer[LSAP_MAX_SIZE_TEXT_SID];
+
+ sprintf(Buffer, "S-%u-", (USHORT) ISid->Revision );
+ strcpy(TextSid, Buffer);
+
+ if ((ISid->IdentifierAuthority.Value[0] != 0) ||
+ (ISid->IdentifierAuthority.Value[1] != 0)) {
+
+ sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)ISid->IdentifierAuthority.Value[0],
+ (USHORT)ISid->IdentifierAuthority.Value[1],
+ (USHORT)ISid->IdentifierAuthority.Value[2],
+ (USHORT)ISid->IdentifierAuthority.Value[3],
+ (USHORT)ISid->IdentifierAuthority.Value[4],
+ (USHORT)ISid->IdentifierAuthority.Value[5] );
+ strcat(TextSid, Buffer);
+
+ } else {
+
+ IdentifierAuthorityValue =
+ (ULONG)ISid->IdentifierAuthority.Value[5] +
+ (ULONG)(ISid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(ISid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(ISid->IdentifierAuthority.Value[2] << 24);
+ sprintf(Buffer, "%lu", IdentifierAuthorityValue);
+ strcat(TextSid, Buffer);
+ }
+
+ //
+ // Now format the Sub Authorities (if any) as text.
+ //
+
+ for (Index = 0; Index < (ULONG) ISid->SubAuthorityCount; Index++ ) {
+
+ sprintf(Buffer, "-%lu", ISid->SubAuthority[Index]);
+ strcat(TextSid, Buffer);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+LsapDbSidToUnicodeSid(
+ IN PSID Sid,
+ OUT PUNICODE_STRING SidU,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a Sid to Unicode form and optionally allocates
+ (via MIDL_user_allocate) memory for the string buffer.
+
+Arguments:
+
+ Sid - Pointer to Sid to be translated.
+
+ SidU - Pointer to Unicode string that will receive the Unicode
+ Sid text.
+
+ AllocateDestinationString - If TRUE, the buffer for the destination
+ string will be allocated. If FALSE, it is assummed that the
+ destination Unicode string references a buffer of sufficient size.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG TextSidSize;
+ PSZ TextSid;
+ ANSI_STRING SidAnsi;
+
+ //
+ // First, query the amount of memory required for a buffer that
+ // will hold the Sid as an ASCIIZ character string.
+ //
+
+ TextSidSize = LsapDbGetSizeTextSid(Sid);
+
+ //
+ // Now allocate a buffer for the Text Sid.
+ //
+
+ TextSid = LsapAllocateLsaHeap(TextSidSize);
+
+ if (TextSid == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Convert the Sid to ASCIIZ and place in the buffer.
+ //
+
+ Status = LsapDbSidToTextSid(Sid, TextSid);
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapFreeLsaHeap(TextSid);
+ return Status;
+ }
+
+ //
+ // Now convert the text Sid to Unicode form via ANSI string form.
+ // If we are to allocate the output buffer, do so via the
+ // midl_USER_allocate routine.
+ //
+
+ RtlInitString(&SidAnsi, TextSid);
+
+ if (AllocateDestinationString) {
+
+ SidU->MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&SidAnsi);
+ SidU->Buffer = MIDL_user_allocate( SidU->MaximumLength );
+ SidU->Length = 0;
+ }
+
+ //
+ // Now convert the Ansi String to a Unicode string. The buffer is
+ // already allocated. Free Text Sid buffer before checking conversion
+ // status.
+ //
+
+ Status = RtlAnsiStringToUnicodeString(SidU, &SidAnsi, FALSE);
+ LsapFreeLsaHeap(TextSid);
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (AllocateDestinationString) {
+
+ MIDL_user_free(SidU->Buffer);
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbLookupTranslateUnknownSids(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN ULONG MappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates unmapped Sids to a character representation.
+ If the Domain of a Sid is unknown, the entire Sid is translated,
+ otherwise the Relative Id only is translated.
+
+Parameters:
+
+ Count - Specifies the number of Sids in the array.
+
+ Sids - Pointer to an array of Sids. Some of these will already
+ have been translated.
+
+ ReferencedDomains - Pointer to Referenced Domains List header.
+
+ TranslatedNames - Pointer to structure that references the array of
+ translated names. The nth element of the referenced array
+ corresponds to the nth Sid in the Sids array. Some of the
+ Sids may be already translated and will be ignored. Those that are
+ not yet translated have zero length Unicode structures with NULL
+ buffer pointers. Already translated Sids are ignored.
+
+ MappedCount - Specifies the number of Sids that have already been
+ translated.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ ULONG SidIndex;
+ ULONG UnmappedCount;
+ PSID Sid;
+ UNICODE_STRING NameU;
+ PLSA_TRANSLATED_NAME Names = (PLSA_TRANSLATED_NAME) TranslatedNames->Names;
+ LSAP_MM_FREE_LIST FreeList;
+ ULONG CleanupFreeListOptions = (ULONG) 0;
+ UnmappedCount = Count - MappedCount;
+
+ //
+ // Create FreeList
+ //
+
+ Status = LsapMmCreateFreeList(&FreeList, UnmappedCount);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto TranslateUnknownSidsError;
+ }
+
+ //
+ // Examine the array of Sids, looking for Unknown ones to translate.
+ // Translate any Unknown ones found to character representations,
+ // and stop either when all of them have been accounted for, or when
+ // the end of the array is reached.
+ //
+
+ if (MappedCount == Count) {
+
+ goto TranslateUnknownSidsFinish;
+ }
+
+ if (MappedCount > Count) {
+
+ goto TranslateUnknownSidsError;
+ }
+
+ for (SidIndex = 0, UnmappedCount = Count - MappedCount;
+ (SidIndex < Count) && (UnmappedCount > 0);
+ SidIndex++) {
+
+ Sid = Sids[SidIndex];
+
+ //
+ // If the Sid has already been mapped, ignore it.
+ //
+
+ if (Names[SidIndex].Use != SidTypeUnknown) {
+
+ continue;
+ }
+
+ //
+ // Found an unmapped Sid. If the domain is known, convert the
+ // Relative Id of the Sid to a Unicode String, limited to 8
+ // characters and with leading zeros.
+ //
+
+ if (Names[SidIndex].DomainIndex >= 0) {
+
+ //
+ // Convert the Relative Id to a Unicode Name and store in
+ // the Translation.
+ //
+
+ Status = LsapRtlSidToUnicodeRid( Sid, &NameU );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto TranslateUnknownSidsError;
+ }
+
+ } else {
+
+ //
+ // The Domain is unknown. In this case, convert the whole Sid
+ // to the standard character representation.
+ //
+
+ Status = LsapRtlConvertSidToUnicodeString( Sid, &NameU );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto TranslateUnknownSidsError;
+ }
+ }
+
+ //
+ // Copy the Unicode Name to the output, allocating memory for
+ // its buffer via MIDL_user_allocate
+ //
+
+ Status = LsapRpcCopyUnicodeString(
+ &FreeList,
+ &Names[SidIndex].Name,
+ &NameU
+ );
+
+ RtlFreeUnicodeString(&NameU);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto TranslateUnknownSidsError;
+ }
+
+ //
+ // Decrement the remaining Unmapped Count
+ //
+
+ UnmappedCount--;
+ }
+
+TranslateUnknownSidsFinish:
+
+ //
+ // Delete the Free List, freeing buffers on the list if an error
+ // occurred.
+ //
+
+ LsapMmCleanupFreeList(&FreeList, CleanupFreeListOptions);
+ return(Status);
+
+TranslateUnknownSidsError:
+
+ //
+ // Specify that buffers on Free List are to be freed.
+ //
+
+ CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
+ goto TranslateUnknownSidsFinish;
+}
+
+
+NTSTATUS
+LsarLookupNames(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server worker routine for the LsaLookupNames
+ API.
+
+ The LsaLookupNames API attempts to translate names of domains, users,
+ groups or aliases to Sids. The caller must have POLICY_LOOKUP_NAMES
+ access to the Policy object.
+
+ Names may be either isolated (e.g. JohnH) or composite names containing
+ both the domain name and account name. Composite names must include a
+ backslash character separating the domain name from the account name
+ (e.g. Acctg\JohnH). An isolated name may be either an account name
+ (user, group, or alias) or a domain name.
+
+ Translation of isolated names introduces the possibility of name
+ collisions (since the same name may be used in multiple domains). An
+ isolated name will be translated using the following algorithm:
+
+ If the name is a well-known name (e.g. Local or Interactive), then the
+ corresponding well-known Sid is returned.
+
+ If the name is the Built-in Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Account Domain's name, then that domain's Sid
+ will be returned.
+ /
+ If the name is the Primary Domain's name, then that domain's Sid will
+ be returned.
+
+ If the name is a user, group, or alias in the Built-in Domain, then the
+ Sid of that account is returned.
+
+ If the name is a user, group, or alias in the Primary Domain, then the
+ Sid of that account is returned.
+
+ Otherwise, the name is not translated.
+
+ NOTE: Proxy, Machine, and Trust user accounts are not referenced
+ for name translation. Only normal user accounts are used for ID
+ translation. If translation of other account types is needed, then
+ SAM services should be used directly.
+
+Arguments:
+
+ This function is the LSA server RPC worker routine for the
+ LsaLookupNames API.
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ Count - Specifies the number of names to be translated.
+
+ Names - Pointer to an array of Count Unicode String structures
+ specifying the names to be looked up and mapped to Sids.
+ The strings may be names of User, Group or Alias accounts or
+ domains.
+
+ ReferencedDomains - Receives a pointer to a structure describing the
+ domains used for the translation. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for
+ each translated name, this structure will only contain one
+ component for each domain utilized in the translation.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ TranslatedSids - Pointer to a structure which will (or already) references an array of
+ records describing each translated Sid. The nth entry in this array
+ provides a translation for the nth element in the Names parameter.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupWksta - First Level Lookup performed on a workstation
+ normally configured for Windows-Nt. The lookup searches the
+ Well-Known Sids/Names, and the Built-in Domain and Account Domain
+ in the local SAM Database. If not all Sids or Names are
+ identified, performs a "handoff" of a Second level Lookup to the
+ LSA running on a Controller for the workstation's Primary Domain
+ (if any).
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids or Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ MappedCount - Pointer to location that contains a count of the Names
+ mapped so far. On exit, this will be updated.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully and all Names have
+ been translated to Sids.
+
+ STATUS_SOME_NOT_MAPPED - At least one of the names provided was
+ trasnlated to a Sid, but not all names could be translated. This
+ is a success status.
+
+ STATUS_NONE_MAPPED - None of the names provided could be translated
+ to Sids. This is an error status, but output is returned. Such
+ output includes partial translations of names whose domain could
+ be identified.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the call.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ BOOLEAN PolicyHandleReferencedHere = FALSE;
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+ PPOLICY_LSA_SERVER_ROLE PolicyLsaServerRoleInfo = NULL;
+ PTRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
+ LSA_HANDLE ControllerPolicyHandle = NULL;
+ ULONG DomainIndex;
+ LSAPR_TRUST_INFORMATION TrustInformation;
+ LSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation;
+ LSAPR_TRUST_INFORMATION AccountDomainTrustInformation;
+ LSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation;
+ ULONG NullNameCount = 0;
+ ULONG NameIndex;
+ PLSA_TRANSLATED_SID OutputSids;
+ PLSAPR_TRUST_INFORMATION Domains = NULL;
+ ULONG OutputSidsLength;
+ ULONG CompletelyUnmappedCount = Count;
+ ULONG IsolatedNameCount;
+ ULONG MappedIsolatedNameCount = (ULONG) 0;
+ ULONG LocalDomainsToSearch = 0;
+
+ PLSAPR_UNICODE_STRING PrefixNames = NULL;
+ PLSAPR_UNICODE_STRING SuffixNames = NULL;
+ LSAPR_UNICODE_STRING BackSlash;
+
+ BuiltInDomainTrustInformation.Name.Buffer = NULL;
+ BuiltInDomainTrustInformation.Sid = NULL;
+
+ AccountDomainTrustInformation.Name.Buffer = NULL;
+ AccountDomainTrustInformation.Sid = NULL;
+
+ PrimaryDomainTrustInformation.Name.Buffer = NULL;
+ PrimaryDomainTrustInformation.Sid = NULL;
+
+ //
+ // If there are no completely unmapped Names remaining, return.
+ //
+
+ if (CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupNamesFinish;
+ }
+
+ //
+ // Names provided are either Isolated, consisting of a single
+ // component, or composite, having the form
+ //
+ // <DomainName>\<SuffixName>
+ //
+ // Split the list of names into two separate arrays, one containing
+ // the Domain Prefixes (or NULL strings) and the other array
+ // containing the Terminal Names. Both arrays are the same size
+ // as the original. First, allocate memory for the output arrays
+ // of UNICODE_STRING structures.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ PrefixNames = MIDL_user_allocate( Count * sizeof( UNICODE_STRING ));
+
+ if (PrefixNames == NULL) {
+
+ goto LookupNamesError;
+ }
+
+ SuffixNames = MIDL_user_allocate( Count * sizeof( UNICODE_STRING ));
+
+ if (SuffixNames == NULL) {
+
+ goto LookupNamesError;
+ }
+
+ RtlInitUnicodeString( (PUNICODE_STRING) &BackSlash, L"\\" );
+
+ LsapRtlSplitNames(
+ (PUNICODE_STRING) Names,
+ Count,
+ (PUNICODE_STRING) &BackSlash,
+ (PUNICODE_STRING) PrefixNames,
+ (PUNICODE_STRING) SuffixNames
+ );
+
+ //
+ // Count the Isolated Names.
+ //
+
+ IsolatedNameCount = (ULONG) 0;
+
+ for (NameIndex = 0; NameIndex < Count; NameIndex++) {
+
+ if (PrefixNames[NameIndex].Length == (USHORT) 0) {
+
+ IsolatedNameCount++;
+ }
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference he handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_LOOKUP_NAMES,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // We can dereference the original PolicyHandle and release the lock on
+ // the LSA Database; if we need to access the database again, we'll
+ // use the trusted Lsa handle and the appropriate API will take
+ // the lock as required.
+ //
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+
+ TranslatedSids->Sids = NULL;
+ TranslatedSids->Entries = 0;
+ *ReferencedDomains = NULL;
+
+ //
+ // Allocate Output Sids array buffer.
+ //
+
+ OutputSidsLength = Count * sizeof(LSA_TRANSLATED_SID);
+ OutputSids = MIDL_user_allocate(OutputSidsLength);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (OutputSids == NULL) {
+
+ goto LookupNamesError;
+ }
+
+ Status = STATUS_SUCCESS;
+ TranslatedSids->Entries = Count;
+ TranslatedSids->Sids = OutputSids;
+
+ //
+ // Initialize the Output Sids array. Zeroise all fields, then
+ // Mark all of the Output Sids as being unknown initially and
+ // set the DomainIndex fields to a negative number meaning
+ // "no domain"
+ //
+
+ RtlZeroMemory( OutputSids, OutputSidsLength);
+
+ for (NameIndex = 0; NameIndex < Count; NameIndex++) {
+
+ OutputSids[NameIndex].Use = SidTypeUnknown;
+ OutputSids[NameIndex].DomainIndex = LSA_UNKNOWN_INDEX;
+ }
+
+ //
+ // Create an empty Referenced Domain List.
+ //
+
+ Status = LsapDbLookupCreateListReferencedDomains( ReferencedDomains, 0 );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Obtain the Trust Information for the
+ // Built-in, Account and Primary Domains.
+ //
+
+ Status = LsapDbLookupLocalDomains(
+ &BuiltInDomainTrustInformation,
+ &AccountDomainTrustInformation,
+ &PrimaryDomainTrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // The local domains to be searched always include the Accounts
+ // domain. For initial lookup targets only, the BUILT_IN domain is
+ // also searched.
+ //
+
+ LocalDomainsToSearch = LSAP_DB_SEARCH_ACCOUNT_DOMAIN;
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ LocalDomainsToSearch |= LSAP_DB_SEARCH_BUILT_IN_DOMAIN;
+
+ //
+ // This is the lowest Lookup Level, normally targeted at a
+ // Workstation.
+ //
+
+ if (IsolatedNameCount > (ULONG) 0) {
+
+ Status = LsapDbLookupIsolatedNames(
+ Count,
+ IsolatedNameCount,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ &BuiltInDomainTrustInformation,
+ &AccountDomainTrustInformation,
+ &PrimaryDomainTrustInformation,
+ *ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ &CompletelyUnmappedCount,
+ &MappedIsolatedNameCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+ }
+
+ //
+ // If all Names are now mapped or partially mapped, or only zero
+ // length names remain, finish.
+ //
+
+ NullNameCount = 0;
+
+ for( NameIndex = 0; NameIndex < Count; NameIndex++) {
+
+ if (Names[NameIndex].Length == 0) {
+
+ NullNameCount++;
+ }
+ }
+
+ if (CompletelyUnmappedCount == NullNameCount) {
+
+ goto LookupNamesFinish;
+ }
+ }
+
+ //
+ // There are some remaining unmapped Names. They may belong to a
+ // local SAM Domain. Currently, there are two such domains, the
+ // Built-in Domain and the Accounts Domain. Search these
+ // domains now, excluding the BUILT_IN domain from higher level
+ // searches.
+ //
+
+ Status = LsapDbLookupNamesInLocalDomains(
+ Count,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ &BuiltInDomainTrustInformation,
+ &AccountDomainTrustInformation,
+ *ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ &CompletelyUnmappedCount,
+ LocalDomainsToSearch
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // If all Names apart from NULL names are now mapped, finish.
+ //
+
+ if (CompletelyUnmappedCount == NullNameCount) {
+
+ goto LookupNamesFinish;
+ }
+
+ //
+ // Not all of the Names have been identified in the local domain(s).
+ // The next step in the search depends on the level of this lookup
+ // and how we are configured as follows:
+ //
+ // Lookup Level Configuration Lookup search next
+ //
+ // LsapLookupWksta Win Nt Primary Domain
+ // LanMan Nt Trusted Domains
+ //
+ // LsapLookupPDC Win Nt error
+ // LanMan Nt Trusted Domains
+ //
+ // LsaLookupTDL Win Nt error
+ // LanMan Nt none
+ //
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ if (LsapProductType != NtProductLanManNt) {
+
+ //
+ // If there is no Primary Domain as in the case of a WORKGROUP,
+ // just finish up. Set a default result code STATUS_SUCCESS.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ if (PrimaryDomainTrustInformation.Sid == NULL) {
+
+ goto LookupNamesFinish;
+ }
+
+ //
+ // There is a Primary Domain. Search it for Names. Since a
+ // Primary Domain is also a Trusted Domain, we use the
+ // Trusted Domain search routine. This routine will "hand off"
+ // the search to a Domain Controller's LSA.
+ //
+
+ RtlCopyMemory(
+ &TrustInformation.Name,
+ &PrimaryDomainTrustInformation.Name,
+ sizeof( UNICODE_STRING)
+ );
+
+ TrustInformation.Sid = (PSID) PrimaryDomainTrustInformation.Sid;
+
+ Status = LsapDbLookupNamesInPrimaryDomain(
+ Count,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ &TrustInformation,
+ *ReferencedDomains,
+ TranslatedSids,
+ LsapLookupPDC,
+ MappedCount,
+ &CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+
+ goto LookupNamesFinish;
+ }
+ }
+
+ //
+ // We reach here in two cases:
+ //
+ // * Initial Level lookups targeted at DC's
+ // * Higher Level Lookups (must be targeted at DC's)
+ //
+ // For the highest level lookup, that on an individual TDC, there
+ // is no more searching to do, since we have already searched the
+ // Accounts Domain and we do not follow trust relationships on DC's
+ // beyond one level.
+ //
+
+ if (LookupLevel == LsapLookupTDL) {
+
+ goto LookupNamesFinish;
+ }
+
+ //
+ // We are either the initial target of the lookup but not configured
+ // as a workstation, or we are the target of a Primary Domain
+ // level lookup. In either case, we must be configured as a DC.
+ //
+
+ Status = STATUS_DOMAIN_CTRLR_CONFIG_ERROR;
+
+ if (LsapProductType != NtProductLanManNt) {
+
+ goto LookupNamesError;
+ }
+
+ //
+ // Search all of the Trusted Domains
+ //
+
+ Status = LsapDbLookupNamesInTrustedDomains(
+ Count,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ *ReferencedDomains,
+ TranslatedSids,
+ LsapLookupTDL,
+ MappedCount,
+ &CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesError;
+ }
+
+LookupNamesFinish:
+
+ //
+ // If some but not all Names were mapped, return informational status
+ // STATUS_SOME_NOT_MAPPED. If no Names were mapped, return error
+ // STATUS_NONE_MAPPED. Note that we expect and STATUS_NONE_MAPPED
+ // errors returned by called routines to have been suppressed before
+ // we get here. The reason for this is that we need to calculate
+ // the return Status based on the whole set of Names, not some subset
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ if (*MappedCount < Count) {
+
+ Status = STATUS_SOME_NOT_MAPPED;
+
+ if (*MappedCount == 0) {
+
+ Status = STATUS_NONE_MAPPED;
+ }
+ }
+ }
+
+ //
+ // If we opened a handle to the Primary Domain's TrustedDomain
+ // object, close it.
+ //
+
+ if (TrustedDomainHandle != NULL) {
+
+ SecondaryStatus = LsarClose( &TrustedDomainHandle );
+ TrustedDomainHandle == NULL;
+
+ //
+ // If the close failed, go to error, returning its status if the
+ // primary status was a success code.
+ //
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ goto LookupNamesError;
+ }
+ }
+
+ //
+ // If necessary, free memory allocated for the Account and
+ // Primary Domain Name and Sid buffers. The memory for the Built-In
+ // Domain Name and Sid buffers is not freed since these are global
+ // data in the Well Known Sid table.
+ //
+
+ if ( AccountDomainTrustInformation.Name.Buffer != NULL ) {
+
+ MIDL_user_free( AccountDomainTrustInformation.Name.Buffer );
+ }
+
+ if ( AccountDomainTrustInformation.Sid != NULL ) {
+
+ MIDL_user_free( AccountDomainTrustInformation.Sid );
+ }
+
+ if ( PrimaryDomainTrustInformation.Name.Buffer != NULL ) {
+
+ MIDL_user_free( PrimaryDomainTrustInformation.Name.Buffer );
+ }
+
+ if ( PrimaryDomainTrustInformation.Sid != NULL ) {
+
+ MIDL_user_free( PrimaryDomainTrustInformation.Sid );
+ }
+
+ //
+ // If necessary, free the arrays of PrefixNames and SuffixNames
+ //
+
+ if (PrefixNames != NULL) {
+
+ MIDL_user_free(PrefixNames);
+ }
+
+ if (SuffixNames != NULL) {
+
+ MIDL_user_free(SuffixNames);
+ }
+
+ return(Status);
+
+LookupNamesError:
+
+ //
+ // If the LookupLevel is the lowest (Workstation Level) free up
+ // the Sids and Referenced Domains arrays.
+ //
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ //
+ // If necessary, free the Sids array.
+ //
+
+ if (TranslatedSids->Sids != NULL) {
+
+ MIDL_user_free( TranslatedSids->Sids );
+ TranslatedSids->Sids = NULL;
+ }
+
+ //
+ // If necessary, free the Referenced Domain List.
+ //
+
+ if (*ReferencedDomains != NULL) {
+
+ Domains = (*ReferencedDomains)->Domains;
+
+ if (Domains != NULL) {
+
+ for (DomainIndex = 0;
+ DomainIndex < (*ReferencedDomains)->Entries;
+ DomainIndex++) {
+
+ if (Domains[ DomainIndex ].Name.Buffer != NULL) {
+
+ MIDL_user_free( Domains[ DomainIndex ].Name.Buffer );
+ Domains[ DomainIndex ].Name.Buffer == NULL;
+ }
+
+ if (Domains[ DomainIndex ].Sid != NULL) {
+
+ MIDL_user_free( Domains[ DomainIndex ].Sid );
+ Domains[ DomainIndex ].Sid == NULL;
+ }
+ }
+ }
+
+ MIDL_user_free( *ReferencedDomains );
+ *ReferencedDomains = NULL;
+ }
+ }
+
+ //
+ // If the primary status was a success code, but the secondary
+ // status was an error, propagate the secondary status.
+ //
+
+ if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto LookupNamesFinish;
+}
+
+
+NTSTATUS
+LsapDbEnumerateNames(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAP_DB_NAME_ENUMERATION_BUFFER DbEnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function enumerates Names of objects of a given type within a container
+ object. Since there may be more information than can be returned in a
+ single call of the routine, multiple calls can be made to get all of the
+ information. To support this feature, the caller is provided with a
+ handle that can be used across calls. On the initial call,
+ EnumerationContext should point to a variable that has been initialized
+ to 0.
+
+Arguments:
+
+ ContainerHandle - Handle to a container object.
+
+ ObjectTypeId - Type of object to be enumerated. The type must be one
+ for which all objects have Names.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ DbEnumerationBuffer - Receives a pointer to a structure that will receive
+ the count of entries returned in an enumeration information array, and
+ a pointer to the array. Currently, the only information returned is
+ the object Names. These Names may be used together with object type to
+ open the objects and obtain any further information available.
+
+ PreferedMaximumLength - prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+ CountReturned - Pointer to variable which will receive a count of the
+ entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if there are no more objects to enumerate. Note that
+ zero or more objects may be enumerated on a call that returns this
+ reply.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_ENUMERATION_ELEMENT LastElement;
+ PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement, FreeElement;
+ ULONG DataLengthUsed;
+ ULONG ThisBufferLength;
+ PUNICODE_STRING Names = NULL;
+ BOOLEAN PreferedMaximumReached = FALSE;
+ ULONG EntriesRead;
+ ULONG Index, EnumerationIndex;
+ BOOLEAN TrustedClient = ((LSAP_DB_HANDLE) ContainerHandle)->Trusted;
+
+ LastElement.Next = NULL;
+ FirstElement = &LastElement;
+
+ //
+ // If no enumeration buffer provided, return an error.
+ //
+
+ if ( (!ARGUMENT_PRESENT(DbEnumerationBuffer)) ||
+ (!ARGUMENT_PRESENT(EnumerationContext )) ) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+ //
+ // Enumerate objects, stopping when the length of data to be returned
+ // reaches or exceeds the Prefered Maximum Length, or reaches the
+ // absolute maximum allowed for LSA object enumerations. We allow
+ // the last object enumerated to bring the total amount of data to
+ // be returned beyond the Prefered Maximum Length, but not beyond the
+ // absolute maximum length.
+ //
+
+ EnumerationIndex = *EnumerationContext;
+
+ for(DataLengthUsed = 0, EntriesRead = 0;
+ DataLengthUsed < PreferedMaximumLength;
+ DataLengthUsed += ThisBufferLength, EntriesRead++) {
+
+ //
+ // If the absolute maximum length has been exceeded, back off
+ // the last object enumerated.
+ //
+
+ if ((DataLengthUsed > LSA_MAXIMUM_ENUMERATION_LENGTH) &&
+ (!TrustedClient)) {
+
+ FirstElement = NextElement->Next;
+ MIDL_user_free(NextElement);
+ break;
+ }
+
+ //
+ // Allocate memory for next enumeration element. Set the Name
+ // field to NULL for cleanup purposes.
+ //
+
+ NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));
+
+ if (NextElement == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ //
+ // Find the next object's Name, and fill in its object information.
+ // Note that memory will be allocated via MIDL_user_allocate
+ // and must be freed when no longer required.
+ //
+
+ Status = LsapDbFindNextName(
+ ContainerHandle,
+ &EnumerationIndex,
+ ObjectTypeId,
+ (PLSAPR_UNICODE_STRING) &NextElement->Name
+ );
+
+ //
+ // Stop the enumeration if any error or warning occurs. Note
+ // that the warning STATUS_NO_MORE_ENTRIES will be returned when
+ // we've gone beyond the last index.
+ //
+
+ if (Status != STATUS_SUCCESS) {
+
+ break;
+ }
+
+ //
+ // Get the length of the data allocated for the object's Name
+ //
+
+ ThisBufferLength = NextElement->Name.Length;
+
+ //
+ // Link the object just found to the front of the enumeration list
+ //
+
+ NextElement->Next = FirstElement;
+ FirstElement = NextElement;
+ }
+
+ //
+ // If an error other than STATUS_NO_MORE_ENTRIES occurred, return it.
+ //
+
+ if ((Status != STATUS_NO_MORE_ENTRIES) && !NT_SUCCESS(Status)) {
+
+ goto EnumerateNamesError;
+ }
+
+ //
+ // The enumeration is complete or has terminated because of return
+ // buffer limitations. If no entries were read, return.
+ //
+
+ if (EntriesRead != 0) {
+
+
+ //
+ // Some entries were read, allocate an information buffer for returning
+ // them.
+ //
+
+ Names = MIDL_user_allocate( sizeof (UNICODE_STRING) * EntriesRead );
+
+ if (Names == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto EnumerateNamesError;
+ }
+
+ //
+ // Memory was successfully allocated for the return buffer.
+ // Copy in the enumerated Names.
+ //
+
+ for (NextElement = FirstElement, Index = 0;
+ NextElement != &LastElement;
+ NextElement = NextElement->Next, Index++) {
+
+ ASSERT(Index < EntriesRead);
+
+ Names[Index] = NextElement->Name;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // No entries available this call.
+ //
+
+ Status = STATUS_NO_MORE_ENTRIES;
+
+ }
+
+EnumerateNamesFinish:
+
+ //
+ // Free the enumeration element structures (if any).
+ //
+
+ for (NextElement = FirstElement; NextElement != &LastElement;) {
+
+ //
+ // If an error has occurred, dispose of memory allocated
+ // for any Names.
+ //
+
+ if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {
+
+ if (NextElement->Name.Buffer != NULL) {
+
+ MIDL_user_free(NextElement->Name.Buffer);
+ }
+ }
+
+ //
+ // Free the memory allocated for the enumeration element.
+ //
+
+ FreeElement = NextElement;
+ NextElement = NextElement->Next;
+
+ MIDL_user_free(FreeElement);
+ }
+
+ //
+ // Fill in return enumeration structure (0 and NULL in error case).
+ //
+
+ DbEnumerationBuffer->EntriesRead = EntriesRead;
+ DbEnumerationBuffer->Names = Names;
+ *EnumerationContext = EnumerationIndex;
+
+ return(Status);
+
+EnumerateNamesError:
+
+ //
+ // If necessary, free memory allocated for returning the Names.
+ //
+
+ if (Names != NULL) {
+
+ MIDL_user_free( Names );
+ Names = NULL;
+ }
+
+ goto EnumerateNamesFinish;
+}
+
+
+VOID
+LsapDbUpdateCountCompUnmappedNames(
+ OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function updates the count of completely unmapped names in a
+ name lookup operation. A name is completely unmapped if its domain
+ is unknown.
+
+Arguments:
+
+ TranslatedSids - Pointer to a structure which will be initialized to
+ reference an array of records describing each translated Sid. The
+ nth entry in this array provides a translation for the nth element in
+ the Names parameter.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ CompletelyUnmappedCount - Pointer to location that will receive
+ a count of completely unmapped Sids. A Name is completely unmapped
+ if it is isolated and unknown, or is composite and its Domain Prefix
+ component is not recognized as a Domain Name.
+
+Return Values:
+
+ None
+
+--*/
+
+{
+ ULONG Count = TranslatedSids->Entries;
+ ULONG SidIndex;
+ ULONG UpdatedCompletelyUnmappedCount = 0;
+
+ for (SidIndex = 0; SidIndex < Count; SidIndex++) {
+
+ if (TranslatedSids->Sids[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX) {
+
+ UpdatedCompletelyUnmappedCount++;
+ }
+ }
+
+ ASSERT(UpdatedCompletelyUnmappedCount <= *CompletelyUnmappedCount);
+ *CompletelyUnmappedCount = UpdatedCompletelyUnmappedCount;
+}
+
+
+NTSTATUS
+LsapDbFindNextName(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ OUT PLSAPR_UNICODE_STRING NextName
+ )
+
+/*++
+
+Routine Description:
+
+ This function finds the next Name of object of a given type within a
+ container object. The given object type must be one where objects
+ have Names. The Names returned can be used on subsequent open calls to
+ access the objects.
+
+Arguments:
+
+ ContainerHandle - Handle to container object.
+
+ EnumerationContext - Pointer to a variable containing the index of
+ the object to be found. A zero value indicates that the first
+ object is to be found.
+
+ ObjectTypeId - Type of the objects whose Names are being enumerated.
+ Ccurrently, this is restricted to objects (such as Secret Objects)
+ that are accessed by Name only.
+
+ NextName - Pointer to Unicode String that will be initialized to
+ reference the next Name found.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - Invalid ContainerHandle specified
+
+ STATUS_NO_MORE_ENTRIES - Warning that no more entries exist.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ ULONG NameKeyValueLength = 0;
+ LSAPR_UNICODE_STRING SubKeyNameU;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ContDirKeyHandle = NULL;
+
+
+ //
+ // Setup object attributes for opening the appropriate Containing
+ // Directory. For example, if we're looking for Account objects,
+ // the containing Directory is "Accounts". The Unicode strings for
+ // containing Directories are set up during Lsa Initialization.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &LsapDbContDirs[ObjectTypeId],
+ OBJ_CASE_INSENSITIVE,
+ ((LSAP_DB_HANDLE) ContainerHandle)->KeyHandle,
+ NULL
+ );
+
+ //
+ // If the object type is not accessed by Name only, return an error.
+ // Currently, only Secret objects have this property.
+ //
+
+
+ if (ObjectTypeId != SecretObject) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ Status = RtlpNtOpenKey(
+ &ContDirKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Initialize the Unicode String in which the next object's Logical Name
+ // will be returned. The Logical Name of an object equals its Registry
+ // Key relative to its Containing Directory, and is also equal to
+ // the Relative Id of the object represented in character form as an
+ // 8-digit number with leading zeros.
+ //
+ // NOTE: The size of buffer allocated for the Logical Name must be
+ // calculated dynamically when the Registry supports long names, because
+ // it is possible that the Logical Name of an object will be equal to a
+ // character representation of the full Name, not just the Relative Id
+ // part.
+ //
+
+ SubKeyNameU.MaximumLength = (USHORT) LSAP_DB_LOGICAL_NAME_MAX_LENGTH;
+ SubKeyNameU.Length = 0;
+ SubKeyNameU.Buffer = LsapAllocateLsaHeap(SubKeyNameU.MaximumLength);
+
+ if (SubKeyNameU.Buffer == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+
+ //
+ // Now enumerate the next subkey.
+ //
+
+ Status = RtlpNtEnumerateSubKey(
+ ContDirKeyHandle,
+ (PUNICODE_STRING) &SubKeyNameU,
+ *EnumerationContext,
+ NULL
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ (*EnumerationContext)++;
+
+ //
+ // Return the Name.
+ //
+
+ *NextName = SubKeyNameU;
+
+ } else {
+
+ //
+ // Not successful - free the subkey name buffer
+ // Note that STATUS_NO_MORE_ENTRIES is a warning
+ // (not a success) code.
+ //
+
+ LsapFreeLsaHeap( SubKeyNameU.Buffer );
+
+ //
+ // Set the out parameter so RPC doesn't try
+ // to return anything.
+ //
+
+ NextName->Length = NextName->MaximumLength = 0;
+ NextName->Buffer = NULL;
+
+ }
+
+ }
+
+ //
+ // Close the containing directory handle
+ //
+
+ SecondaryStatus = NtClose(ContDirKeyHandle);
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+ }
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsapDbLookupIsolatedNames(
+ IN ULONG Count,
+ IN ULONG IsolatedNameCount,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to identify isolated names as the names of well known
+ Sids or Domains present on the Lookup Path.
+
+ Names may be either isolated (e.g. JohnH) or composite names containing
+ both the domain name and account name. Composite names must include a
+ backslash character separating the domain name from the account name
+ (e.g. Acctg\JohnH). An isolated name may be either an account name
+ (user, group, or alias) or a domain name.
+
+ Translation of isolated names introduces the possibility of name
+ collisions (since the same name may be used in multiple domains). An
+ isolated name will be translated using the following algorithm:
+
+ If the name is a well-known name (e.g. Local or Interactive), then the
+ corresponding well-known Sid is returned.
+
+ If the name is the Built-in Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Account Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Primary Domain's name, then that domain's Sid will
+ be returned.
+
+ If the name is the name of one of the Primary Domain's Trusted Domains,
+ then that domain's Sid will be returned.
+
+ If the name is a user, group, or alias in the Built-in Domain, then the
+ Sid of that account is returned.
+
+ If the name is a user, group, or alias in the Primary Domain, then the
+ Sid of that account is returned.
+
+ Otherwise, the name is not translated.
+
+ NOTE: Proxy, Machine, and Trust user accounts are not referenced
+ for name translation. Only normal user accounts are used for ID
+ translation. If translation of other account types is needed, then
+ SAM services should be used directly.
+
+Arguments:
+
+ Count - Specifies the number of names to be translated.
+
+ IsolatedNameCount - Specifies the number of Isolated Names present
+ in the list of Names. An Isolated Name is a Name that contains
+ only one component, e.g. "ScottBi". Names such as "NtDev\ScottBi"
+ containing two components are called Composite Names.
+
+ Names - Pointer to an array of Count Unicode String structures
+ specifying the names to be looked up and mapped to Sids.
+ The strings may be names of User, Group or Alias accounts or
+ domains.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ ReferencedDomains - Pointer to a structure in which the list of domains
+ used in the translation is maintained. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for each
+ translated name, this structure will only contain one component for
+ each domain utilized in the translation.
+
+ TranslatedSids - Pointer to a structure in which the translations to Sids
+ corresponding to the Names specified on Names is maintained. The
+ nth entry in this array provides a translation (where known) for the
+ nth element in the Names parameter.
+
+ MappedCount - Pointer to location in which a count of the Names that
+ have been completely translated is maintained.
+
+ CompletelyUnmappedCount - Pointer to a location in which a count of the
+ Names that have not been translated (either partially, by identification
+ of a Domain Prefix, or completely) is maintained.
+
+ MappedIsolatedNameCount - Pointer to a location in which a count of the
+ Isolated Names that have been mapped is maintained.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // First, lookup any Isolated Well Known Names
+ //
+
+ Status = LsapDbLookupIsolatedWellKnownNames(
+ Count,
+ IsolatedNameCount,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ CompletelyUnmappedCount,
+ MappedIsolatedNameCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupIsolatedNamesError;
+ }
+
+ //
+ // If all of the Isolated Names have now been mapped, finish.
+ //
+
+ if (*MappedIsolatedNameCount == IsolatedNameCount) {
+
+ goto LookupIsolatedNamesFinish;
+ }
+
+ //
+ // Next, attempt to identify Isolated Names as Domain Names
+ //
+
+ Status = LsapDbLookupIsolatedDomainNames(
+ Count,
+ IsolatedNameCount,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ BuiltInDomainTrustInformation,
+ AccountDomainTrustInformation,
+ PrimaryDomainTrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ CompletelyUnmappedCount,
+ MappedIsolatedNameCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupIsolatedNamesError;
+ }
+
+LookupIsolatedNamesFinish:
+
+ return(Status);
+
+LookupIsolatedNamesError:
+
+ goto LookupIsolatedNamesFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupIsolatedWellKnownNames(
+ IN ULONG Count,
+ IN ULONG IsolatedNameCount,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to identify isolated names as the names of well known
+ Sids.
+
+ Names may be either isolated (e.g. JohnH) or composite names containing
+ both the domain name and account name. Composite names must include a
+ backslash character separating the domain name from the account name
+ (e.g. Acctg\JohnH). An isolated name may be either an account name
+ (user, group, or alias) or a domain name.
+
+ Translation of isolated names introduces the possibility of name
+ collisions (since the same name may be used in multiple domains). An
+ isolated name will be translated using the following algorithm:
+
+ If the name is a well-known name (e.g. Local or Interactive), then the
+ corresponding well-known Sid is returned.
+
+ If the name is the Built-in Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Account Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Primary Domain's name, then that domain's Sid will
+ be returned.
+
+ If the name is the name of one of the Primary Domain's Trusted Domains,
+ then that domain's Sid will be returned.
+
+ If the name is a user, group, or alias in the Built-in Domain, then the
+ Sid of that account is returned.
+
+ If the name is a user, group, or alias in the Primary Domain, then the
+ Sid of that account is returned.
+
+ Otherwise, the name is not translated.
+
+ NOTE: Proxy, Machine, and Trust user accounts are not referenced
+ for name translation. Only normal user accounts are used for ID
+ translation. If translation of other account types is needed, then
+ SAM services should be used directly.
+
+Arguments:
+
+ Count - Specifies the number of names to be translated.
+
+ IsolatedNameCount - Specifies the number of Isolated Names present
+ in the list of Names. An Isolated Name is a Name that contains
+ only one component, e.g. "ScottBi". Names such as "NtDev\ScottBi"
+ containing two components are called Composite Names.
+
+ Names - Pointer to an array of Count Unicode String structures
+ specifying the names to be looked up and mapped to Sids.
+ The strings may be names of User, Group or Alias accounts or
+ domains.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ ReferencedDomains - Pointer to a structure in which the list of domains
+ used in the translation is maintained. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for each
+ translated name, this structure will only contain one component for
+ each domain utilized in the translation.
+
+ TranslatedSids - Pointer to a structure in which the translations to Sids
+ corresponding to the Names specified on Names is maintained. The
+ nth entry in this array provides a translation (where known) for the
+ nth element in the Names parameter.
+
+ MappedCount - Pointer to location in which a count of the Names that
+ have been completely translated is maintained.
+
+ CompletelyUnmappedCount - Pointer to a location in which a count of the
+ Names that have not been translated (either partially, by identification
+ of a Domain Prefix, or completely) is maintained.
+
+ MappedIsolatedNameCount - Pointer to a location in which a count of the
+ Isolated Names that have been mapped is maintained.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Names may remain partially or completely unmapped.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG UpdatedMappedCount;
+ ULONG NameNumber;
+ ULONG UnmappedNamesRemaining;
+ PLSA_TRANSLATED_SID OutputSids;
+ LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
+ LSAPR_TRUST_INFORMATION TrustInformation;
+ UCHAR SubAuthorityCount;
+ PLSAPR_SID Sid = NULL;
+ PLSAPR_SID PrefixSid = NULL;
+ ULONG PrefixSidLength;
+ ULONG RelativeId;
+ OutputSids = TranslatedSids->Sids;
+
+ //
+ // Initialize output parameters.
+ //
+
+ *MappedCount = UpdatedMappedCount = 0;
+ UnmappedNamesRemaining = Count - UpdatedMappedCount;
+
+ //
+ // Attempt to identify Names as Well Known Isolated Names
+ //
+
+ for (NameNumber = 0;
+ (NameNumber < Count) && (UnmappedNamesRemaining > 0);
+ NameNumber++) {
+
+ //
+ // Examine the next entry in the Names array. If the corresponding
+ // translated Sid entry has SidTypeUnknown for its Use field, the
+ // name has not been translated.
+ //
+
+ if (OutputSids[NameNumber].Use == SidTypeUnknown) {
+
+ //
+ // Attempt to identify the name as the name of a Well Known Sid
+ // by using the Well Known Sids table. We skip entries in the
+ // table for Sids that are also in the Built In domain. For
+ // those, we drop through to the Built in Domain search. Note
+ // that only one of these, the Administrators alias is currently
+ // present in the table.
+ //
+
+ if (LsapDbLookupIndexWellKnownName(
+ &Names[NameNumber],
+ &WellKnownSidIndex
+ ) && (WellKnownSidIndex != LsapAliasAdminsSidIndex)) {
+
+ //
+ // Name is identified. Obtain its Sid. If the
+ // SubAuthorityCount for the Sid is positive, extract the
+ // Relative Id and place in the translated Sid entry,
+ // otherwise store LSA_UNKNOWN_INDEX there.
+ //
+
+ Sid = LsapDbWellKnownSid(WellKnownSidIndex);
+
+ SubAuthorityCount = *RtlSubAuthorityCountSid((PSID) Sid);
+
+ RelativeId = LSA_UNKNOWN_ID;
+
+ PrefixSid = NULL;
+
+ //
+ // Get the Sid's Use.
+ //
+
+ OutputSids[NameNumber].Use =
+ LsapDbWellKnownSidNameUse(WellKnownSidIndex);
+
+ //
+ // If the Sid is a Domain Sid, store pointer to
+ // it in the Trust Information.
+ //
+
+ if (OutputSids[NameNumber].Use == SidTypeDomain) {
+
+ TrustInformation.Sid = Sid;
+
+ } else {
+
+ //
+ // The Sid is not a domain Sid. Construct the Relative Id
+ // and Prefix Sid. This is equal to the original Sid
+ // excluding the lowest subauthority (Relative id).
+ //
+
+ if (SubAuthorityCount > 0) {
+
+ RelativeId = *RtlSubAuthoritySid((PSID) Sid, SubAuthorityCount - 1);
+ }
+
+ PrefixSidLength = RtlLengthRequiredSid(
+ SubAuthorityCount - 1
+ );
+
+
+ PrefixSid = MIDL_user_allocate( PrefixSidLength );
+
+ if (PrefixSid == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+
+ RtlCopyMemory( PrefixSid, Sid, PrefixSidLength );
+
+ (*RtlSubAuthorityCountSid( (PSID) PrefixSid ))--;
+
+ TrustInformation.Sid = PrefixSid;
+ }
+
+ //
+ // Set the Relative Id. For a Domain Sid this is set to the
+ // Unknown Value.
+ //
+
+ OutputSids[NameNumber].RelativeId = RelativeId;
+
+ //
+ // Lookup this Domain Sid or Prefix Sid in the Referenced Domain
+ // List. If it is already there, return the DomainIndex for the
+ // existing entry and free up the memory allocated for the
+ // Prefix Sid (if any).
+ //
+
+ if (LsapDbLookupListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation.Sid,
+ (PLONG) &OutputSids[NameNumber].DomainIndex
+ )) {
+
+ UnmappedNamesRemaining--;
+
+ if (PrefixSid != NULL) {
+
+ MIDL_user_free( PrefixSid );
+ PrefixSid = NULL;
+ }
+
+ continue;
+ }
+
+ //
+ // This Domain or Prefix Sid is not currently on the
+ // Referenced Domain List. Complete a Trust Information
+ // entry and add it to the List. Copy in the Domain Name
+ // (Domain Sids) or NULL string. Note that we use
+ // RtlCopyMemory to copy a UNICODE_STRING structure onto
+ // a LSAPR_UNICODE_STRING structure.
+ //
+
+ RtlCopyMemory(
+ &TrustInformation.Name,
+ &WellKnownSids[WellKnownSidIndex].DomainName,
+ sizeof(UNICODE_STRING)
+ );
+
+ //
+ // Make an entry in the list of Referenced Domains. Note
+ // that in the case of well-known Sids, the Prefix Sid
+ // may or may not be the Sid of a Domain. For those well
+ // known Sids whose Prefix Sid is not a domain Sid, the
+ // Name field in the Trust Information has been set to the
+ // NULL string.
+ //
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ &TrustInformation,
+ (PLONG) &OutputSids[NameNumber].DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // If we allocated memory for a Prefix Sid, free it.
+ //
+
+ if (PrefixSid != NULL) {
+
+ MIDL_user_free( PrefixSid );
+ PrefixSid = NULL;
+ }
+
+ UnmappedNamesRemaining--;
+ (*MappedIsolatedNameCount)++;
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupIsolatedWellKnownNamesError;
+ }
+
+ //
+ // Set the output parameters in the success case..
+ //
+
+ TranslatedSids->Sids = OutputSids;
+ TranslatedSids->Entries = Count;
+ *MappedCount = Count - UnmappedNamesRemaining;
+ *CompletelyUnmappedCount = UnmappedNamesRemaining;
+
+LookupIsolatedWellKnownNamesFinish:
+
+ //
+ // If we still have memory allocated for the a Prefix Sid, free it.
+ //
+
+ if (PrefixSid != NULL) {
+
+ MIDL_user_free( PrefixSid );
+ PrefixSid = NULL;
+ }
+
+ return(Status);
+
+LookupIsolatedWellKnownNamesError:
+
+ goto LookupIsolatedWellKnownNamesFinish;
+}
+
+
+BOOLEAN
+LsapDbLookupIndexWellKnownName(
+ IN OPTIONAL PLSAPR_UNICODE_STRING Name,
+ OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up a Name to determine if it is well-known. If so,
+ an index into the table of well-known Sids is returned.
+
+Arguments:
+
+ Name - Pointer to Name to be looked up. If a NULL pointer or
+ pointer to a zero length string is specified, FALSE will
+ always be returned.
+
+ WellKnownSidIndex - Pointer to variable that will receive the
+ index of the Name if well known.
+
+Return Value:
+
+ BOOLEAN - TRUE if the Name is well-known, else FALSE
+
+--*/
+
+{
+ LSAP_WELL_KNOWN_SID_INDEX Index;
+ PLSAPR_UNICODE_STRING MatchName = NULL;
+ BOOLEAN BooleanStatus = FALSE;
+
+ if ((!ARGUMENT_PRESENT(Name)) || Name->Length == 0 ) {
+
+ return(FALSE);
+ }
+
+ //
+ // Scan the table of well-known Sids looking for a match on Name.
+ //
+
+ for(Index = LsapWorldSidIndex; Index<LsapDummyLastSidIndex; Index++) {
+
+ //
+ // Allow NULL entries in the table of well-known Sids for now.
+ //
+
+ if (WellKnownSids[Index].Sid == NULL) {
+
+ continue;
+ }
+
+ //
+ // If the current entry in the table of Well Known Sids
+ // is for a Domain Sid, match the name with the DomainName
+ // field. Otherwise, match it with the Name field.
+ //
+
+ if (WellKnownSids[Index].Use == SidTypeDomain) {
+
+ MatchName = (PLSAPR_UNICODE_STRING) &WellKnownSids[Index].DomainName;
+
+ if (RtlEqualDomainName(
+ (PUNICODE_STRING) Name,
+ (PUNICODE_STRING) MatchName
+ )) {
+
+ //
+ // If a match is found, return the index to the caller.
+ //
+
+ BooleanStatus = TRUE;
+ break;
+ }
+
+ } else {
+
+ MatchName = (PLSAPR_UNICODE_STRING) &WellKnownSids[Index].Name;
+
+ if (RtlEqualUnicodeString(
+ (PUNICODE_STRING) Name,
+ (PUNICODE_STRING) MatchName,
+ TRUE
+ )) {
+
+ //
+ // If a match is found, return the index to the caller.
+ //
+
+ BooleanStatus = TRUE;
+ break;
+ }
+ }
+ }
+
+ *WellKnownSidIndex = Index;
+
+ return(BooleanStatus);
+}
+
+
+PUNICODE_STRING
+LsapDbWellKnownSidName(
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the Unicode Name of a Well Known Sid.
+
+Arguments:
+
+ WellKnownSidIndex - Index into the Well Known Sid information table.
+ It is the caller's responsibility to ensure that the given index
+ is valid.
+
+Return Value:
+
+ PUNICODE_STRING Pointer to the name of the Well Known Sid.
+
+--*/
+
+{
+ //
+ // If the Sid is a Domain Sid, its name is contained within the
+ // DomainName field in the Well Known Sids table. If the Sid is not a
+ // Domain Sid, its name is contained within the Name field of the entry.
+ //
+
+ if (WellKnownSids[WellKnownSidIndex].Use == SidTypeDomain) {
+
+ return(&WellKnownSids[WellKnownSidIndex].DomainName);
+
+ } else {
+
+ return(&WellKnownSids[WellKnownSidIndex].Name);
+ }
+}
+
+
+NTSTATUS
+LsapDbLookupIsolatedDomainNames(
+ IN ULONG Count,
+ IN ULONG IsolatedNameCount,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to identify isolated names as the names of Domains.
+
+ Names may be either isolated (e.g. JohnH) or composite names containing
+ both the domain name and account name. Composite names must include a
+ backslash character separating the domain name from the account name
+ (e.g. Acctg\JohnH). An isolated name may be either an account name
+ (user, group, or alias) or a domain name.
+
+ Translation of isolated names introduces the possibility of name
+ collisions (since the same name may be used in multiple domains). An
+ isolated name will be translated using the following algorithm:
+
+ If the name is a well-known name (e.g. Local or Interactive), then the
+ corresponding well-known Sid is returned.
+
+ If the name is the Built-in Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Account Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Primary Domain's name, then that domain's Sid will
+ be returned.
+
+ If the name is the name of one of the Primary Domain's Trusted Domains,
+ then that domain's Sid will be returned.
+
+ If the name is a user, group, or alias in the Built-in Domain, then the
+ Sid of that account is returned.
+
+ If the name is a user, group, or alias in the Primary Domain, then the
+ Sid of that account is returned.
+
+ Otherwise, the name is not translated.
+
+ NOTE: Proxy, Machine, and Trust user accounts are not referenced
+ for name translation. Only normal user accounts are used for ID
+ translation. If translation of other account types is needed, then
+ SAM services should be used directly.
+
+Arguments:
+
+ Count - Specifies the number of names to be translated.
+
+ IsolatedNameCount - Specifies the number of Isolated Names present
+ in the list of Names. An Isolated Name is a Name that contains
+ only one component, e.g. "ScottBi". Names such as "NtDev\ScottBi"
+ containing two components are called Composite Names.
+
+ Names - Pointer to an array of Count Unicode String structures
+ specifying the names to be looked up and mapped to Sids.
+ The strings may be names of User, Group or Alias accounts or
+ domains.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ ReferencedDomains - Pointer to a structure in which the list of domains
+ used in the translation is maintained. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for each
+ translated name, this structure will only contain one component for
+ each domain utilized in the translation.
+
+ TranslatedSids - Pointer to a structure in which the translations to Sids
+ corresponding to the Names specified on Names is maintained. The
+ nth entry in this array provides a translation (where known) for the
+ nth element in the Names parameter.
+
+ MappedCount - Pointer to location in which a count of the Names that
+ have been completely translated is maintained.
+
+ CompletelyUnmappedCount - Pointer to a location in which a count of the
+ Names that have not been translated (either partially, by identification
+ of a Domain Prefix, or completely) is maintained.
+
+ MappedIsolatedNameCount - Pointer to a location in which a count of the
+ Isolated Names that have been mapped is maintained.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the call.
+--*/
+
+{
+ NTSTATUS
+ Status = STATUS_SUCCESS,
+ IgnoreStatus;
+
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+
+ ULONG
+ NameIndex;
+
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO
+ PolicyAccountDomainInfo = NULL;
+
+ PLSAPR_TRUST_INFORMATION
+ TrustInformation = NULL;
+
+ LSAP_DB_TRUSTED_DOMAIN_LIST
+ PDTrustedDomainList;
+
+ PLSAP_DB_TRUSTED_DOMAIN_LIST
+ TrustedDomainList = NULL;
+
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION
+ TrustedDomainListSection = NULL;
+
+ BOOLEAN
+ PrimaryDomainAccessible = TRUE;
+
+ LSA_HANDLE
+ ControllerPolicyHandle = NULL;
+
+ //
+ // Search for Isolated Names that match the Built-In Domain Name,
+ // Account Domain Name or one of the Primary Domain's Trusted Domain
+ // Names.
+ //
+
+ PDTrustedDomainList.Valid = FALSE;
+
+ for (NameIndex = 0;
+ (NameIndex < Count) && (*MappedIsolatedNameCount < IsolatedNameCount);
+ NameIndex++) {
+
+ //
+ // Skip this name if already mapped or partially mapped.
+ //
+
+ if (!LsapDbCompletelyUnmappedSid(&TranslatedSids->Sids[NameIndex])) {
+
+ continue;
+ }
+
+ //
+ // Skip this name if composite.
+ //
+
+ if (PrefixNames[ NameIndex ].Length != (USHORT) 0) {
+
+ continue;
+ }
+
+ //
+ // We've found an Isolated Name. First check if it is the
+ // name of the Built In Domain.
+ //
+
+ Status = LsapDbLookupIsolatedDomainName(
+ NameIndex,
+ &SuffixNames[NameIndex],
+ BuiltInDomainTrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ CompletelyUnmappedCount,
+ MappedIsolatedNameCount
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ continue;
+ }
+
+ if (Status != STATUS_NONE_MAPPED) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Isolated Name is not the name of the Built-in Domain. See if
+ // it is the name of the Accounts Domain.
+ //
+
+ Status = LsapDbLookupIsolatedDomainName(
+ NameIndex,
+ &SuffixNames[NameIndex],
+ AccountDomainTrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ CompletelyUnmappedCount,
+ MappedIsolatedNameCount
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ continue;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // If we are configured as a member of a Work Group, there is
+ // no Primary or Trusted Domain List to search, so skip to next
+ // name. We are configured as a member of a Work Group if and
+ // only if out PolicyPrimaryDomainInformation contains a NULL Sid.
+ //
+
+ if (PrimaryDomainTrustInformation->Sid == NULL) {
+
+ continue;
+ }
+
+ //
+ // Isolated Name is not the name of either the Built-in or Accounts
+ // Domain. Try the Primary Domain if this is different from the
+ // Accounts Domain.
+ //
+
+ if (!RtlEqualDomainName(
+ (PUNICODE_STRING) &PrimaryDomainTrustInformation->Name,
+ (PUNICODE_STRING) &AccountDomainTrustInformation->Name
+ )) {
+
+
+ Status = LsapDbLookupIsolatedDomainName(
+ NameIndex,
+ &SuffixNames[NameIndex],
+ PrimaryDomainTrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ CompletelyUnmappedCount,
+ MappedIsolatedNameCount
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ continue;
+ }
+
+ if (Status != STATUS_NONE_MAPPED) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // See if the Isolated Name matches one of the Primary Domain's
+ // Trusted Domains or, if we are a DC, one of our Trusted Domains.
+ // If we are a Workstation, we first need to enumerate the Trusted Domains
+ // on the Primary Domain. If the Name is a Trusted Domain,
+ // translate it.
+ //
+
+ if (LsapProductType != NtProductLanManNt) {
+
+ if ((!PDTrustedDomainList.Valid) && PrimaryDomainAccessible) {
+
+ //
+ // We are a Workstation. Use the Trusted Domain List
+ // for the Primary Domain after first constructing a copy
+ // of it.
+ //
+
+ TrustedDomainList = &PDTrustedDomainList;
+
+ //
+ // Open the Policy object on some DC for the Primary Domain.
+ //
+
+ Status = LsapDbOpenPolicyTrustedDomain(
+ PrimaryDomainTrustInformation,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &ControllerPolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // We are unable to connect to the Primary Domain.
+ // This simply means that we limit our search for Isolated
+ // Names to the local domains, so mask the error and continue.
+ //
+
+ Status = STATUS_SUCCESS;
+ PrimaryDomainAccessible = FALSE;
+ TrustedDomainList = NULL;
+ continue;
+ }
+
+
+ //
+ // We've successfully opened a DC for the Primary Domain.
+ // Now build a list of its Trusted Domains.
+ //
+
+ Status = LsapDbBuildTrustedDomainList(
+ ControllerPolicyHandle,
+ TrustedDomainList
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ PDTrustedDomainList.Valid = FALSE;
+ PrimaryDomainAccessible = FALSE;
+ SecondaryStatus = LsaClose(ControllerPolicyHandle);
+ ControllerPolicyHandle = NULL;
+ continue;
+ }
+ }
+
+ } else {
+
+ ASSERT (LsapProductType == NtProductLanManNt);
+
+ //
+ // We are a DC. Use the Local Trusted Domain List.
+ //
+
+ TrustedDomainList = NULL;
+
+ }
+
+ //
+ // Lookup the Isolated Name in the appropriate Trusted Domain List.
+ //
+
+ Status = LsapDbLookupNameTrustedDomainList(
+ TrustedDomainList,
+ &SuffixNames[NameIndex],
+ &TrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_SUCH_DOMAIN) {
+
+ break;
+ }
+
+ //
+ // This Isolated Name is not the Name of a Trusted Domain.
+ // Skip to the next Name.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ if ( ControllerPolicyHandle != NULL) {
+
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+
+ }
+
+ continue;
+ }
+
+ //
+ // This Isolated Name is a Trusted Domain. Translate it.
+ //
+
+ Status = LsapDbLookupIsolatedDomainName(
+ NameIndex,
+ &SuffixNames[NameIndex],
+ TrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ MappedCount,
+ CompletelyUnmappedCount,
+ MappedIsolatedNameCount
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ if ( ControllerPolicyHandle != NULL) {
+
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+
+ }
+
+ continue;
+ }
+
+ if (Status != STATUS_NONE_MAPPED) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupIsolatedDomainNamesError;
+ }
+
+LookupIsolatedDomainNamesFinish:
+
+ //
+ // Clean up any trusted domain list we might have created.
+ //
+
+ if (PDTrustedDomainList.Valid) {
+ IgnoreStatus = LsapDbDestroyTrustedDomainList( &PDTrustedDomainList );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ }
+
+ if ( ControllerPolicyHandle != NULL) {
+
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ if (NT_SUCCESS(Status)) {
+ Status = SecondaryStatus;
+ }
+ }
+ }
+
+
+ return(Status);
+
+LookupIsolatedDomainNamesError:
+
+ goto LookupIsolatedDomainNamesFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupAddListReferencedDomains(
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ OUT PLONG DomainIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This function searches a Referenced Domain List for an entry for a
+ given domain and, if no entry exists, adds new entry. If an entry
+ id added, its index into the Referenced Domain List is returned,
+ otherwise the index of the existing entry is returned. If an entry
+ needs to be added and there is insufficient room in the list provided
+ for the new entry, the list will be created or grown as necessary.
+
+Arguments:
+
+ ReferencedDomains - Pointer to a structure in which the list of domains
+ used in the translation is maintained. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for each
+ translated name, this structure will only contain one component for
+ each domain utilized in the translation.
+
+ TrustInformation - Points to Trust Information for the domain being
+ added to the list. On exit, the DomainIndex parameter will be set to the
+ index of the entry on the Referenced Domain List; a negative
+ value will be stored in the error case.
+
+ DomainIndex - Pointer to location that receives the index of the
+ newly added or existing entry for the domain within the
+ Referenced Domain List. In the error case, a negative value
+ is returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG NextIndex;
+ LSAPR_TRUST_INFORMATION OutputTrustInformation;
+
+ OutputTrustInformation.Name.Buffer = NULL;
+ OutputTrustInformation.Sid = NULL;
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (ReferencedDomains == NULL) {
+
+ goto AddListReferencedDomainsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Search the existing list, trying to match the Domain Sid in the
+ // provided Trust Information with the Domain Sid in a Referenced Domain
+ // List entry. If an entry is found with matching Sid, just return
+ // that entry's index.
+ //
+
+ if (LsapDbLookupListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation->Sid,
+ DomainIndex
+ )) {
+
+ goto AddListReferencedDomainsFinish;
+ }
+
+ //
+ // Check that there is enough room in the List provided for one more
+ // entry. If not, grow the list, copying and freeing the old.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ if (ReferencedDomains->Entries >= ReferencedDomains->MaxEntries) {
+
+ Status = LsapDbLookupGrowListReferencedDomains(
+ ReferencedDomains,
+ ReferencedDomains->MaxEntries +
+ LSAP_DB_REF_DOMAIN_DELTA
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto AddListReferencedDomainsError;
+ }
+ }
+
+ //
+ // We now have a Referenced Domain List with room for at least one more
+ // entry. Copy in the Trust Information.
+ //
+
+ NextIndex = ReferencedDomains->Entries;
+
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ (PUNICODE_STRING) &OutputTrustInformation.Name,
+ (PUNICODE_STRING) &TrustInformation->Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto AddListReferencedDomainsError;
+ }
+
+ Status = LsapRpcCopySid(
+ NULL,
+ (PSID) &OutputTrustInformation.Sid,
+ (PSID) TrustInformation->Sid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto AddListReferencedDomainsError;
+ }
+
+ ReferencedDomains->Domains[NextIndex] = OutputTrustInformation;
+ *DomainIndex = (LONG) NextIndex;
+ ReferencedDomains->Entries++;
+
+AddListReferencedDomainsFinish:
+
+ return(Status);
+
+AddListReferencedDomainsError:
+
+ //
+ // Cleanup buffers allocated for Output Trust Information structure.
+ //
+
+ if (OutputTrustInformation.Name.Buffer != NULL) {
+
+ MIDL_user_free( OutputTrustInformation.Name.Buffer );
+ }
+
+ if (OutputTrustInformation.Sid != NULL) {
+
+ MIDL_user_free( OutputTrustInformation.Sid );
+ }
+
+ goto AddListReferencedDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupCreateListReferencedDomains(
+ OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ IN ULONG InitialMaxEntries
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates an empty Referenced Domain List. The caller
+ is responsible for cleaning up this list when no longer required.
+
+Arguments:
+
+ ReferencedDomains - Receives a pointer to the newly created empty
+ Referenced Domain List.
+
+ InitialMaxEntries - Initial maximum number of entries desired.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient System Resources
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG DomainsLength;
+ PLSAPR_TRUST_INFORMATION Domains = NULL;
+ PVOID Buffers[2];
+ ULONG BufferCount;
+ ULONG Index;
+ PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
+
+ //
+ // Allocate the Referenced Domain List header.
+ //
+
+ BufferCount = 0;
+
+ OutputReferencedDomains = MIDL_user_allocate(
+ sizeof(LSAP_DB_REFERENCED_DOMAIN_LIST)
+ );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (OutputReferencedDomains == NULL) {
+
+ goto CreateListReferencedDomainsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ Buffers[BufferCount] = OutputReferencedDomains;
+ BufferCount++;
+
+ //
+ // If a non-zero initial entry count, allocate an array of Trust Information
+ // entries.
+ //
+
+ if (InitialMaxEntries > 0) {
+
+ DomainsLength = sizeof(LSA_TRUST_INFORMATION) * InitialMaxEntries;
+ Domains = MIDL_user_allocate( DomainsLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (Domains == NULL) {
+
+ goto CreateListReferencedDomainsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ Buffers[BufferCount] = Domains;
+ BufferCount++;
+
+ RtlZeroMemory( Domains, DomainsLength );
+ }
+
+ //
+ // Initialize the Referenced Domain List Header
+ //
+
+ OutputReferencedDomains->Entries = 0;
+ OutputReferencedDomains->MaxEntries = InitialMaxEntries;
+ OutputReferencedDomains->Domains = Domains;
+
+CreateListReferencedDomainsFinish:
+
+ *ReferencedDomains = OutputReferencedDomains;
+ return(Status);
+
+CreateListReferencedDomainsError:
+
+ //
+ // Free up buffers allocated bu this routine.
+ //
+
+ for (Index = 0; Index < BufferCount; Index++) {
+
+ MIDL_user_free(Buffers[Index]);
+ }
+
+ goto CreateListReferencedDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupGrowListReferencedDomains(
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN ULONG MaxEntries
+ )
+
+/*++
+
+Routine Description:
+
+ This function expands a Referenced Domain List to contain the
+ specified maximum number of entries. The memory for the old Domains
+ array is released.
+
+Arguments:
+
+ ReferencedDomains - Pointer to a Referenced Domain List. This
+ list references an array of zero or more Trust Information
+ entries describing each of the domains referenced by the names.
+ This array will be appended to/reallocated if necessary.
+
+ MaxEntries - New maximum number of entries.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
+ complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAPR_TRUST_INFORMATION NewDomainsInfo = NULL;
+ PLSAPR_TRUST_INFORMATION OldDomainsInfo = NULL;
+ ULONG OldDomainsInfoLength, NewDomainsInfoLength;
+
+ if (ReferencedDomains->MaxEntries < MaxEntries) {
+
+ NewDomainsInfoLength = MaxEntries * sizeof (LSA_TRUST_INFORMATION);
+ OldDomainsInfoLength =
+ ReferencedDomains->MaxEntries * sizeof (LSA_TRUST_INFORMATION);
+
+ NewDomainsInfo = MIDL_user_allocate( NewDomainsInfoLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (NewDomainsInfo == NULL) {
+
+ goto GrowListReferencedDomainsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // If there was an existing Trust Information Array, copy it
+ // to the newly allocated one and free it.
+ //
+
+ OldDomainsInfo = ReferencedDomains->Domains;
+
+ if (OldDomainsInfo != NULL) {
+
+ RtlCopyMemory( NewDomainsInfo, OldDomainsInfo, OldDomainsInfoLength );
+ MIDL_user_free( OldDomainsInfo );
+ }
+
+ ReferencedDomains->Domains = NewDomainsInfo;
+ ReferencedDomains->MaxEntries = MaxEntries;
+ }
+
+GrowListReferencedDomainsFinish:
+
+ return(Status);
+
+GrowListReferencedDomainsError:
+
+ goto GrowListReferencedDomainsFinish;
+}
+
+
+BOOLEAN
+LsapDbLookupListReferencedDomains(
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN PLSAPR_SID DomainSid,
+ OUT PLONG DomainIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This function searches a Referenced Domain List for a given domain
+ and, if found, returns the index of the domain's entry in the list.
+ If the domain is not found an error is returned and a negative value
+ is returned.
+
+Arguments:
+
+ ReferencedDomains - Pointer to a Referenced Domain List. This
+ list references an array of zero or more Trust Information
+ entries describing each of the domains referenced by the names.
+ This array will be appended to/reallocated if necessary.
+
+ DomainSid - Information containing the Domain's Sid.
+
+ DomainIndex - Pointer to location that receives the index of the domain
+ in the Referenced Domain List if the domin is found, otherwise
+ a negative value.
+
+Return Values:
+
+ BOOLEAN - TRUE if entry found, else FALSE.
+
+--*/
+
+{
+ BOOLEAN BooleanStatus = FALSE;
+ LONG Index;
+ LONG Entries;
+ PLSAPR_TRUST_INFORMATION DomainsInfo;
+
+ //
+ // Search the Referenced Domain List by Sid or by Name
+ //
+
+ Entries = (LONG) ReferencedDomains->Entries;
+ DomainsInfo = ReferencedDomains->Domains;
+ *DomainIndex = LSA_UNKNOWN_INDEX;
+
+ for (Index = 0; Index < (LONG) Entries; Index++) {
+
+ if (RtlEqualSid( (PSID) DomainsInfo[Index].Sid, (PSID) DomainSid)) {
+
+ BooleanStatus = TRUE;
+ *DomainIndex = Index;
+ break;
+ }
+ }
+
+ return(BooleanStatus);
+}
+
+
+NTSTATUS
+LsapDbLookupMergeDisjointReferencedDomains(
+ IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST FirstReferencedDomainList,
+ IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST SecondReferencedDomainList,
+ OUT PLSAPR_REFERENCED_DOMAIN_LIST *OutputReferencedDomainList,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function merges disjoint Referenced Domain Lists, producing a third
+ list. The output list is always produced in non allocate(all_nodes) form.
+
+Arguments:
+
+ FirstReferencedDomainList - Pointer to first mergand.
+
+ SecondReferencedDomainList - Pointer to second mergand.
+
+ OutputReferencedDomainList - Receives a pointer to the output list.
+
+ Options - Specifies optional actions
+
+ LSAP_DB_USE_FIRST_MERGAND_GRAPH - Specifies that the resulting
+ merged Referenced Domain List may reference the graph of
+ pointers in the first Referenced Domain list. This option
+ is normally selected, since that graph has been allocated
+ as individual nodes.
+
+ LSAP_DB_USE_SECOND_MERGAND_GRAPH - Specifies that the resulting
+ merged Referenced Domain List may reference the graph of
+ pointers in the first Referenced Domain list. This option
+ is normally not selected, since that graph is usually allocated
+ as all_nodes.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG TotalEntries;
+ ULONG FirstReferencedDomainListLength;
+ ULONG SecondReferencedDomainListLength;
+ ULONG FirstEntries, SecondEntries;
+ LSAP_MM_FREE_LIST FreeList;
+ ULONG NextEntry;
+ ULONG MaximumFreeListEntries;
+ ULONG CleanupFreeListOptions = (ULONG) 0;
+
+ //
+ // Calculate Size of output Referenced Domain List.
+ //
+
+ FirstEntries = (ULONG) 0;
+
+ if (FirstReferencedDomainList != NULL) {
+
+ FirstEntries = FirstReferencedDomainList->Entries;
+ }
+
+ SecondEntries = (ULONG) 0;
+
+ if (SecondReferencedDomainList != NULL) {
+
+ SecondEntries = SecondReferencedDomainList->Entries;
+ }
+
+ TotalEntries = FirstEntries + SecondEntries;
+
+ //
+ // Allocate a Free List for error cleanup. We need two entries
+ // per Referenced Domain List entry, one for the Domain Name buffer
+ // and one for the Domain Sid.
+ //
+
+ MaximumFreeListEntries = (ULONG) 0;
+
+ if (!(Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH)) {
+
+ MaximumFreeListEntries += 2*FirstEntries;
+ }
+
+ if (!(Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH)) {
+
+ MaximumFreeListEntries += 2*SecondEntries;
+ }
+
+ Status = LsapMmCreateFreeList( &FreeList, MaximumFreeListEntries );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MergeDisjointDomainsError;
+ }
+
+ Status = LsapDbLookupCreateListReferencedDomains(
+ OutputReferencedDomainList,
+ TotalEntries
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MergeDisjointDomainsError;
+ }
+
+ //
+ // Set the number of entries used. We will use all of the entries,
+ // so set this value to the Maximum number of Entries.
+ //
+
+ (*OutputReferencedDomainList)->Entries = TotalEntries;
+
+ //
+ // Copy in the entries (if any) from the first list.
+ //
+
+ FirstReferencedDomainListLength =
+ FirstEntries * sizeof(LSA_TRUST_INFORMATION);
+
+ if (FirstReferencedDomainListLength > (ULONG) 0) {
+
+ if (Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH) {
+
+ RtlCopyMemory(
+ (*OutputReferencedDomainList)->Domains,
+ FirstReferencedDomainList->Domains,
+ FirstReferencedDomainListLength
+ );
+
+ } else {
+
+ //
+ // The graph of the first Referenced Domain List must be
+ // copied to separately allocated memory buffers.
+ // Copy each of the Trust Information entries, allocating
+ // individual memory buffers for each Domain Name and Sid.
+ //
+
+ for (NextEntry = 0; NextEntry < FirstEntries; NextEntry++) {
+
+ Status = LsapRpcCopyUnicodeString(
+ &FreeList,
+ (PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[NextEntry].Name),
+ (PUNICODE_STRING) &FirstReferencedDomainList->Domains[NextEntry].Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ Status = LsapRpcCopySid(
+ &FreeList,
+ (PSID) &((*OutputReferencedDomainList)->Domains[NextEntry].Sid),
+ (PSID) FirstReferencedDomainList->Domains[NextEntry].Sid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MergeDisjointDomainsError;
+ }
+ }
+ }
+
+ //
+ // Copy in the entries (if any) from the second list.
+ //
+
+ SecondReferencedDomainListLength =
+ SecondEntries * sizeof(LSA_TRUST_INFORMATION);
+
+ if (SecondReferencedDomainListLength > (ULONG) 0) {
+
+ if (Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH) {
+
+ RtlCopyMemory(
+ (*OutputReferencedDomainList)->Domains + FirstReferencedDomainList->Entries,
+ SecondReferencedDomainList->Domains,
+ SecondReferencedDomainListLength
+ );
+
+ } else {
+
+ //
+ // Copy each of the Trust Information entries, allocating
+ // individual memory buffers for each Domain Name and Sid.
+ //
+
+ for (NextEntry = 0; NextEntry < SecondEntries; NextEntry++) {
+
+ Status = LsapRpcCopyUnicodeString(
+ &FreeList,
+ (PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Name),
+ (PUNICODE_STRING) &SecondReferencedDomainList->Domains[NextEntry].Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ Status = LsapRpcCopySid(
+ &FreeList,
+ (PSID) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Sid),
+ (PSID) SecondReferencedDomainList->Domains[NextEntry].Sid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MergeDisjointDomainsError;
+ }
+ }
+ }
+
+MergeDisjointDomainsFinish:
+
+ //
+ // Delete the Free List, freeing buffers on the list if an error
+ // occurred.
+ //
+
+ LsapMmCleanupFreeList( &FreeList, CleanupFreeListOptions );
+ return(Status);
+
+MergeDisjointDomainsError:
+
+ //
+ // Specify that buffers on Free List are to be freed.
+ //
+
+ CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
+ goto MergeDisjointDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupSidsInLocalDomains(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up Sids in the local SAM domains and attempts to
+ translate them to names. Currently, there are two local SAM domains,
+ the Built-in domain (which has a well-known Sid and name) and the
+ Account Domain (which has a configurable Sid and name).
+
+Arguments:
+
+ Count - Number of Sids in the Sids array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Sids - Pointer to array of pointers to Sids to be translated.
+ Zero or all of the Sids may already have been translated
+ elsewhere. If any of the Sids have been translated, the
+ Names parameter will point to a location containing a non-NULL
+ array of Name translation structures corresponding to the
+ Sids. If the nth Sid has been translated, the nth name
+ translation structure will contain either a non-NULL name
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Sid has not yet been translated, the nth name
+ translation structure will contain a zero-length name string
+ and a negative value for the Referenced Domain List index.
+
+ ReferencedDomains - Pointer to a structure in which the list of domains
+ used in the translation is maintained. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for each
+ translated name, this structure will only contain one component for
+ each domain utilized in the translation.
+
+ TranslatedNames - Pointer to a structure in which the translations to Names
+ corresponding to the Sids specified on Sids is maintained. The
+ nth entry in this array provides a translation (where known) for the
+ nth element in the Sids parameter.
+
+ MappedCount - Pointer to location in which a count of the Names that
+ have been completely translated is maintained.
+
+ CompletelyUnmappedCount - Pointer to a location in which a count of the
+ Names that have not been translated (either partially, by identification
+ of a Domain Prefix, or completely) is maintained.
+
+ Options - Specifies optional actions.
+
+ LSAP_DB_SEARCH_BUILT_IN_DOMAIN - Search the Built In Domain
+
+ LSAP_DB_SEARCH_ACCOUNT_DOMAIN - Search the Account Domain
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Sids may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INTERNAL_DB_ERROR - A corruption has been detected in
+ the LSA Database.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter
+
+ - No handle to the Policy object was provided on a request
+ to search the Account Domain.
+--*/
+
+{
+ NTSTATUS
+ Status,
+ SecondaryStatus = STATUS_SUCCESS;
+
+ LSAPR_TRUST_INFORMATION
+ TrustInformation;
+
+ ULONG
+ UpdatedMappedCount = *MappedCount;
+
+ LSAPR_HANDLE
+ TrustedPolicyHandle = NULL;
+
+ LSAP_WELL_KNOWN_SID_INDEX
+ WellKnownSidIndex;
+
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO
+ PolicyAccountDomainInfo = NULL;
+
+
+ //
+ // If there are no completely unmapped Sids remaining, return.
+ //
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupSidsInLocalDomainsFinish;
+ }
+
+
+
+ //
+ // If requested, lookup Sids in the BUILT-IN Domain.
+ //
+
+ if (Options & LSAP_DB_SEARCH_BUILT_IN_DOMAIN) {
+
+ //
+ // Set up the Trust Information structure for this domain.
+ //
+
+ TrustInformation.Sid = LsapBuiltInDomainSid;
+
+ Status = STATUS_INTERNAL_DB_ERROR;
+
+ if (!LsapDbLookupIndexWellKnownSid(
+ LsapBuiltInDomainSid,
+ &WellKnownSidIndex
+ )) {
+
+ goto LookupSidsInLocalDomainsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Obtain the name of the Built In Domain from the table of
+ // Well Known Sids. It suffices to copy the Unicode structures
+ // since we do not need a separate copy of the name buffer.
+ //
+
+ TrustInformation.Name = *((PLSAPR_UNICODE_STRING)
+ LsapDbWellKnownSidName(WellKnownSidIndex));
+
+ Status = LsapDbLookupSidsInLocalDomain(
+ LSAP_DB_SEARCH_BUILT_IN_DOMAIN,
+ Count,
+ Sids,
+ &TrustInformation,
+ ReferencedDomains,
+ TranslatedNames,
+ &UpdatedMappedCount,
+ CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInLocalDomainsError;
+ }
+
+ //
+ // If all Sids are now mapped or partially mapped, finish.
+ //
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupSidsInLocalDomainsFinish;
+ }
+ }
+
+ //
+ // If requested, search the Account Domain.
+ //
+
+ if (Options & LSAP_DB_SEARCH_ACCOUNT_DOMAIN) {
+
+ //
+ // The Sid and Name of the Account Domain are both configurable, and
+ // we need to obtain them from the Policy Object. Now obtain the
+ // Account Domain Sid and Name by querying the appropriate
+ // Policy Information Class.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInLocalDomainsError;
+ }
+
+ //
+ // Set up the Trust Information structure for the Account Domain.
+ //
+
+ TrustInformation.Sid = PolicyAccountDomainInfo->DomainSid;
+
+ RtlCopyMemory(
+ &TrustInformation.Name,
+ &PolicyAccountDomainInfo->DomainName,
+ sizeof (UNICODE_STRING)
+ );
+
+ //
+ // Now search the Account Domain for more Sid translations.
+ //
+
+ Status = LsapDbLookupSidsInLocalDomain(
+ LSAP_DB_SEARCH_ACCOUNT_DOMAIN,
+ Count,
+ Sids,
+ &TrustInformation,
+ ReferencedDomains,
+ TranslatedNames,
+ &UpdatedMappedCount,
+ CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInLocalDomainsError;
+ }
+ }
+
+LookupSidsInLocalDomainsFinish:
+
+ //
+ // If necessary, free the Policy Account Domain Info
+ //
+
+ if (PolicyAccountDomainInfo != NULL) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION(
+ PolicyAccountDomainInformation,
+ (PLSAPR_POLICY_INFORMATION) PolicyAccountDomainInfo
+ );
+
+ PolicyAccountDomainInfo = NULL;
+ }
+
+
+ //
+ // Return the updated total count of Sids mapped.
+ //
+
+ *MappedCount = UpdatedMappedCount;
+ return(Status);
+
+LookupSidsInLocalDomainsError:
+
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto LookupSidsInLocalDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupSidsInLocalDomain(
+ IN ULONG LocalDomain,
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up Sids in a SAM domain on the local system
+ attempts to translate them to names.
+
+Arguments:
+
+ LocalDomain - Indicates which local domain to look in. Valid values
+ are:
+ LSAP_DB_SEARCH_BUILT_IN_DOMAIN
+ LSAP_DB_SEARCH_ACCOUNT_DOMAIN
+
+ Count - Number of Sids in the Sids array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Sids - Pointer to array of pointers to Sids to be translated.
+ Zero or all of the Sids may already have been translated
+ elsewhere. If any of the Sids have been translated, the
+ Names parameter will point to a location containing a non-NULL
+ array of Name translation structures corresponding to the
+ Sids. If the nth Sid has been translated, the nth name
+ translation structure will contain either a non-NULL name
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Sid has not yet been translated, the nth name
+ translation structure will contain a zero-length name string
+ and a negative value for the Referenced Domain List index.
+
+ TrustInformation - Pointer to Trust Information specifying a Domain Sid
+ and Name.
+
+ ReferencedDomains - Pointer to a structure in which the list of domains
+ used in the translation is maintained. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for each
+ translated name, this structure will only contain one component for
+ each domain utilized in the translation.
+
+ TranslatedNames - Pointer to a structure in which the translations to Names
+ corresponding to the Sids specified on Sids is maintained. The
+ nth entry in this array provides a translation (where known) for the
+ nth element in the Sids parameter.
+
+ MappedCount - Pointer to location in which a count of the Names that
+ have been completely translated is maintained.
+
+ CompletelyUnmappedCount - Pointer to a location in which a count of the
+ Names that have not been translated (either partially, by identification
+ of a Domain Prefix, or completely) is maintained.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Sids may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS
+ Status,
+ SecondaryStatus;
+
+ PLSA_TRANSLATED_NAME
+ OutputNames = NULL;
+
+ SAMPR_HANDLE
+ LocalSamDomainHandle = NULL;
+
+ PLSAPR_UNICODE_STRING
+ Names = NULL;
+
+ PSID_NAME_USE
+ Use = NULL;
+
+ ULONG
+ RelativeIdCount,
+ RelativeIdIndex,
+ SidIndex,
+ LocalMappedCount = (ULONG) 0,
+ DomainSidCount = (ULONG) 0;
+
+ LONG
+ DomainIndex,
+ DomainSidIndexList,
+ NextIndex,
+ TmpIndex;
+
+ PULONG
+ RelativeIds = NULL,
+ SidIndices = NULL;
+
+ PLSAPR_SID
+ DomainSid = TrustInformation->Sid;
+
+ SAMPR_RETURNED_USTRING_ARRAY
+ SamReturnedNames;
+
+ SAMPR_ULONG_ARRAY
+ SamReturnedUses;
+
+ UCHAR
+ SubAuthorityCountDomain;
+
+ PLSAPR_TRUST_INFORMATION
+ FreeTrustInformation = NULL;
+
+
+
+ //
+ // Make sure the SAM handles have been established.
+ //
+
+ Status = LsapOpenSam();
+ ASSERT(NT_SUCCESS(Status));
+
+
+ SamReturnedNames.Count = 0;
+ SamReturnedNames.Element = NULL;
+ SamReturnedUses.Count = 0;
+ SamReturnedUses.Element = NULL;
+
+ OutputNames = (PLSA_TRANSLATED_NAME) TranslatedNames->Names;
+
+ SecondaryStatus = STATUS_SUCCESS;
+
+ if (*MappedCount + *CompletelyUnmappedCount > Count) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto LookupSidsInLocalDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+ goto LookupSidsInLocalDomainFinish;
+ }
+
+ //
+ // Now construct a list of Relative Ids to be looked up. Any Sids that
+ // do not belong to the specified domain are ignored. Any Sids that
+ // are not marked as having unknown Use are ignored, except for certain
+ // Well Known Sids that do not have a Well Known Name. These Sids
+ // have known Use and a name string length of 0.
+ //
+ // First, scan the array of Sids looking for completely unmapped ones
+ // that have the same domain prefix as the local domain we are dealing
+ // with. Note that we can omit any Sid whose Translated Name entry
+ // contains a non-zero DomainIndex since the domain of that Sid has
+ // already been identified. Once the number of Sids is known, allocate
+ // memory for an array of Relative Ids and a parallel array of indices
+ // into the original Sids array. The latter array will be used to locate
+ // entries in the TranslatedNames array to which information returned by
+ // the SamrLookupIdsInDomain() call will be copied.
+ //
+
+ for (RelativeIdCount = 0, SidIndex = 0, DomainSidIndexList = -1;
+ SidIndex < Count;
+ SidIndex++) {
+
+ if ((OutputNames[SidIndex].Use == SidTypeUnknown) &&
+ (OutputNames[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX)) {
+
+ if (LsapRtlPrefixSid( (PSID) DomainSid, (PSID) Sids[SidIndex])) {
+ RelativeIdCount++;
+ } else if (RtlEqualSid( (PSID)DomainSid, (PSID)Sids[SidIndex])) {
+
+ //
+ // This is the domain sid itself. Update
+ // the output information directly, but don't add
+ // it to the list of RIDs to be looked up by SAM.
+ //
+ // NOTE that we don't yet know what our domain index
+ // is. So, just link these entries together and we'll
+ // set the index later.
+ //
+
+ OutputNames[SidIndex].DomainIndex = DomainSidIndexList;
+ DomainSidIndexList = SidIndex;
+ OutputNames[SidIndex].Use = SidTypeDomain;
+ OutputNames[SidIndex].Name.Buffer = NULL;
+ OutputNames[SidIndex].Name.Length = 0;
+ OutputNames[SidIndex].Name.MaximumLength = 0;
+
+ LocalMappedCount++;
+ DomainSidCount++;
+ }
+ }
+ }
+
+ //
+ // If we have any SIDs in this domain, then add it to the
+ // referenced domain list.
+ //
+
+ if ((RelativeIdCount != 0) || (DomainSidCount != 0)) {
+
+ //
+ // At least one Sid has the domain Sid as prefix (or is the
+ // domain SID). Add the domain to the list of Referenced
+ // Domains and obtain a Domain Index back.
+ //
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto LookupSidsInLocalDomainError;
+ }
+
+ //
+ // If any of the sids were this domain's sid, then they
+ // already have their OutputNames[] entry filled in except
+ // that the DomainIndex was unkown. It is now known, so
+ // fill it in. Any such entries to change have been linked
+ // together using DomainSidIndexList as a listhead.
+ //
+
+ for (NextIndex = DomainSidIndexList;
+ NextIndex != -1;
+ NextIndex = TmpIndex ) {
+
+
+ TmpIndex = OutputNames[NextIndex].DomainIndex;
+ OutputNames[NextIndex].DomainIndex = DomainIndex;
+ }
+ }
+
+ //
+ // If any of the remaining Sids have the specified Local
+ // domain Sid as prefix Sid, look them up
+ //
+
+ if (RelativeIdCount != 0) {
+
+ //
+ // Allocate memory for the Relative Id and cross reference arrays
+ //
+
+ RelativeIds = LsapAllocateLsaHeap( RelativeIdCount * sizeof(ULONG));
+
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (RelativeIds == NULL) {
+ goto LookupSidsInLocalDomainError;
+ }
+
+ SidIndices = LsapAllocateLsaHeap( RelativeIdCount * sizeof(ULONG));
+
+ if (SidIndices == NULL) {
+ goto LookupSidsInLocalDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Obtain the SubAuthorityCount for the Domain Sid
+ //
+
+ SubAuthorityCountDomain = *RtlSubAuthorityCountSid( (PSID) DomainSid );
+
+ //
+ // Now setup the array of Relative Ids to be looked up, recording
+ // in the SidIndices array the index of the corresponding Sid within the
+ // original Sids array. Set the DomainIndex field for those Sids
+ // eligible for the SAM lookup.
+ //
+
+ for (RelativeIdIndex = 0, SidIndex = 0;
+ (RelativeIdIndex < RelativeIdCount) && (SidIndex < Count);
+ SidIndex++) {
+
+ if ((OutputNames[SidIndex].Use == SidTypeUnknown) &&
+ (OutputNames[SidIndex].DomainIndex == LSA_UNKNOWN_INDEX)) {
+
+ if (LsapRtlPrefixSid( (PSID) DomainSid, (PSID) Sids[SidIndex] )) {
+
+ SidIndices[RelativeIdIndex] = SidIndex;
+ RelativeIds[RelativeIdIndex] =
+ *RtlSubAuthoritySid(
+ (PSID) Sids[SidIndex],
+ SubAuthorityCountDomain
+ );
+
+ OutputNames[SidIndex].DomainIndex = DomainIndex;
+ RelativeIdIndex++;
+
+ }
+ }
+ }
+
+ //
+ // Lookup the Sids in the specified SAM Domain.
+ //
+
+ if (LocalDomain == LSAP_DB_SEARCH_BUILT_IN_DOMAIN ) {
+ LocalSamDomainHandle = LsapBuiltinDomainHandle;
+ } else {
+ ASSERT(LocalDomain == LSAP_DB_SEARCH_ACCOUNT_DOMAIN);
+ LocalSamDomainHandle = LsapAccountDomainHandle;
+ }
+
+ //
+ // Call SAM to lookup the Relative Id's
+ //
+
+ Status = SamrLookupIdsInDomain(
+ LocalSamDomainHandle,
+ RelativeIdCount,
+ RelativeIds,
+ &SamReturnedNames,
+ &SamReturnedUses
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // The only error allowed is STATUS_NONE_MAPPED. Filter this out.
+ //
+
+ if (Status != STATUS_NONE_MAPPED) {
+ goto LookupSidsInLocalDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Now copy the information returned from SAM into the output
+ // Translated Sids array. As we go, compute a count of the names
+ // mapped by SAM.
+ //
+
+ for (RelativeIdIndex = 0;
+ RelativeIdIndex < SamReturnedNames.Count;
+ RelativeIdIndex++) {
+
+ SidIndex = SidIndices[RelativeIdIndex];
+
+ //
+ // Copy the Sid Use. If the Sid was mapped by this SAM call, copy
+ // its Name and increment the count of Sids mapped by this SAM call.
+ // Note that we don't need to set the DomainIndex since we did so
+ // earlier.
+ //
+
+ OutputNames[SidIndex].Use = SamReturnedUses.Element[RelativeIdIndex];
+
+ if (OutputNames[SidIndex].Use != SidTypeUnknown) {
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ &OutputNames[SidIndex].Name,
+ (PUNICODE_STRING) &SamReturnedNames.Element[RelativeIdIndex]
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ LocalMappedCount++;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInLocalDomainError;
+ }
+
+ }
+
+
+ //
+ // Update the Mapped and Completely Unmapped Counts. To the Mapped Count
+ // add in the number of Sids that SAM completely identified. From the
+ // Completely Unmapped Count subtract the number of Sids presented to
+ // Sam, since all of these will be at least partially translated.
+ //
+
+ *MappedCount += LocalMappedCount;
+ *CompletelyUnmappedCount -= (RelativeIdCount + DomainSidCount);
+
+LookupSidsInLocalDomainFinish:
+
+ //
+ // If necessary, free the Lsa Heap buffer allocated for the RelativeIds
+ // and SidIndices arrays.
+ //
+
+ if (RelativeIds != NULL) {
+
+ LsapFreeLsaHeap( RelativeIds );
+ RelativeIds = NULL;
+ }
+
+ if (SidIndices != NULL) {
+
+ LsapFreeLsaHeap( SidIndices );
+ SidIndices = NULL;
+ }
+
+ //
+ // If necessary, free the Names array returned from SAM.
+ //
+
+ if ( SamReturnedNames.Count != 0 ) {
+
+ SamIFree_SAMPR_RETURNED_USTRING_ARRAY ( &SamReturnedNames );
+ SamReturnedNames.Count = 0;
+ }
+
+ //
+ // If necessary, free the Uses array returned from SAM.
+ //
+
+ if ( SamReturnedUses.Count != 0 ) {
+
+ SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedUses );
+ SamReturnedUses.Count = 0;
+ }
+
+
+ return(Status);
+
+LookupSidsInLocalDomainError:
+
+ //
+ // If necessary, free memory for the OutputTrustInformation Domain
+ // Name Buffer and Sid Buffer.
+ //
+
+ if (DomainIndex >= 0) {
+
+ FreeTrustInformation = &ReferencedDomains->Domains[DomainIndex];
+
+ if (FreeTrustInformation->Sid != NULL) {
+
+ MIDL_user_free( FreeTrustInformation->Sid );
+ FreeTrustInformation->Sid = NULL;
+ }
+
+ if (FreeTrustInformation->Name.Buffer != NULL) {
+
+ MIDL_user_free( FreeTrustInformation->Name.Buffer );
+ FreeTrustInformation->Name.Buffer = NULL;
+ FreeTrustInformation->Name.Length = 0;
+ FreeTrustInformation->Name.MaximumLength = 0;
+ }
+ }
+
+ //
+ // If necessary, free the Name buffer in each of the OutputNames entries
+ // written by this routine and set the Name slot to NULL.
+ //
+
+ for (RelativeIdIndex = 0;
+ RelativeIdIndex < SamReturnedNames.Count;
+ RelativeIdIndex++) {
+
+ SidIndex = SidIndices[RelativeIdIndex];
+
+ if (OutputNames[SidIndex].Name.Buffer != NULL) {
+
+ MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
+ OutputNames[SidIndex].Name.Buffer = NULL;
+ OutputNames[SidIndex].Name.Length = 0;
+ OutputNames[SidIndex].Name.MaximumLength = 0;
+ }
+ }
+
+ //
+ // Restore the Use field for each entry we wrote to back to SidType
+ // Unknown.
+ //
+
+ for (RelativeIdIndex = 0;
+ RelativeIdIndex < SamReturnedNames.Count;
+ RelativeIdIndex++) {
+
+ SidIndex = SidIndices[RelativeIdIndex];
+
+ if (OutputNames[SidIndex].Name.Buffer != NULL) {
+
+ MIDL_user_free( OutputNames[SidIndex].Name.Buffer );
+ OutputNames[SidIndex].Name.Buffer = NULL;
+ OutputNames[SidIndex].Name.Length = 0;
+ OutputNames[SidIndex].Name.MaximumLength = 0;
+ }
+
+ OutputNames[SidIndex].Use = SidTypeUnknown;
+ OutputNames[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto LookupSidsInLocalDomainFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupSidsInPrimaryDomain(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to translate Sids in a Primary Domain. A
+ Trusted Domain object must exist for the domain in the local Policy
+ Database. This object will be used to access the Domain's list of
+ Controllers and one or more callouts will be made to access the LSA
+ Policy Databases on these Controllers.
+
+Arguments:
+
+ Count - Number of Sids in the Sids array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Sids - Pointer to array of pointers to Sids to be translated.
+ Zero or all of the Sids may already have been translated
+ elsewhere. dd esp la If any of the Sids have been translated, the
+
+ Names parameter will point to a location containing a non-NULL
+ array of Name translation structures corresponding to the
+ Sids. If the nth Sid has been translated, the nth name
+ translation structure will contain either a non-NULL name
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Sid has not yet been translated, the nth name
+ translation structure will contain a zero-length name string
+ and a negative value for the Referenced Domain List index.
+
+ TrustInformation - Specifies the name and Sid of the Primary Domain.
+
+ ReferencedDomains - Pointer to a structure in which the list of domains
+ used in the translation is maintained. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for each
+ translated name, this structure will only contain one component for
+ each domain utilized in the translation.
+
+ TranslatedNames - Pointer to a structure in which the translations to Names
+ corresponding to the Sids specified on Sids is maintained. The
+ nth entry in this array provides a translation (where known) for the
+ nth element in the Sids parameter.
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids or Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ NOTE: LsapLookupWksta is not valid for this parameter.
+
+ MappedCount - Pointer to location in which a count of the Names that
+ have been completely translated is maintained.
+
+ CompletelyUnmappedCount - Pointer to a location in which a count of the
+ Names that have not been translated (either partially, by identification
+ of a Domain Prefix, or completely) is maintained.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Sids may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ LSA_HANDLE ControllerPolicyHandle = NULL;
+ PLSAPR_UNICODE_STRING ControllerNames = NULL;
+ ULONG NextLevelCount;
+ ULONG NextLevelMappedCount;
+ ULONG SidIndex;
+ ULONG NextLevelSidIndex;
+ PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
+ PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
+ PLSA_TRANSLATED_NAME NextLevelNames = NULL;
+ PLSAPR_SID *NextLevelSids = NULL;
+ LONG FirstEntryIndex;
+ PULONG SidIndices = NULL;
+ BOOLEAN PartialSidTranslationsAttempted = FALSE;
+
+ //
+ // If there are no completely unmapped Sids remaining, just return.
+ //
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupSidsInPrimaryDomainFinish;
+ }
+
+ //
+ // Open the Policy object on some DC for the Primary Domain.
+ //
+
+ Status = LsapDbOpenPolicyTrustedDomain(
+ TrustInformation,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &ControllerPolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // We cannot access the Primary Domain. Suppress the error
+ // and translate Domain Prefix Sids for Sids belonging to
+ // the Primary Domain.
+ //
+
+ Status = STATUS_SUCCESS;
+ goto LookupSidsInPrimaryDomainFinish;
+ }
+
+ //
+ // We have successfully opened a Domain Controller's Policy
+ // Database. Now prepare to hand off a Sid lookup for the
+ // remaining unmapped Sids to that Controller. Here, this
+ // server side of the LSA is a client of the LSA on the
+ // target controller. We will construct an array of the
+ // remianing unmapped Sids, look them up and then merge the
+ // resulting ReferencedDomains and Translated Names into
+ // our existing list.
+ //
+
+ NextLevelCount = *CompletelyUnmappedCount;
+
+ //
+ // Allocate an array to hold the indices of unmapped Sids
+ // relative to the original Sids and TranslatedNames->Names
+ // arrays.
+ //
+
+ SidIndices = MIDL_user_allocate(NextLevelCount * sizeof(ULONG));
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (SidIndices == NULL) {
+
+ goto LookupSidsInPrimaryDomainError;
+ }
+
+ //
+ // Allocate an array for the Sids to be looked up at the Domain
+ // Controller.
+ //
+
+ NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );
+
+ if (NextLevelSids == NULL) {
+
+ goto LookupSidsInPrimaryDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Now scan the original array of Names and its parallel
+ // Translated Sids array. Copy over any Sids that are
+ // completely unmapped.
+ //
+
+ NextLevelSidIndex = (ULONG) 0;
+
+ for (SidIndex = 0;
+ SidIndex < Count && NextLevelSidIndex < NextLevelCount;
+ SidIndex++) {
+
+ if (LsapDbCompletelyUnmappedName(&TranslatedNames->Names[SidIndex])) {
+
+ NextLevelSids[NextLevelSidIndex] = Sids[SidIndex];
+ SidIndices[NextLevelSidIndex] = SidIndex;
+ NextLevelSidIndex++;
+ }
+ }
+
+ NextLevelMappedCount = (ULONG) 0;
+
+ Status = LsaICLookupSids(
+ ControllerPolicyHandle,
+ NextLevelCount,
+ (PSID *) NextLevelSids,
+ (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
+ &NextLevelNames,
+ LookupLevel,
+ &NextLevelMappedCount
+ );
+
+ //
+ // If the callout to LsaLookupSids() was unsuccessful, disregard
+ // the error and set the domain name for any Sids having this
+ // domain Sid as prefix sid. We still want to return translations
+ // of Sids we have so far even if we are unable to callout to another
+ // LSA.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_SUCCESS;
+ goto LookupSidsInPrimaryDomainFinish;
+ }
+
+ //
+ // The callout to LsaLookupSids() was successful. We now have
+ // an additional list of Referenced Domains containing the
+ // Primary Domain and/or one or more of its Trusted Domains.
+ // Merge the two Referenced Domain Lists together, noting that
+ // since they are disjoint, the second list is simply
+ // concatenated with the first. The index of the first entry
+ // of the second list will be used to adjust all of the
+ // Domain Index entries in the Translated Names entries.
+ // Note that since the memory for the graph of the first
+ // Referenced Domain list has been allocated as individual
+ // nodes, we specify that the nodes in this graph can be
+ // referenced by the output Referenced Domain list.
+ //
+
+ Status = LsapDbLookupMergeDisjointReferencedDomains(
+ ReferencedDomains,
+ NextLevelReferencedDomains,
+ &OutputReferencedDomains,
+ LSAP_DB_USE_FIRST_MERGAND_GRAPH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInPrimaryDomainError;
+ }
+
+ FirstEntryIndex = ReferencedDomains->Entries;
+
+ //
+ // Now update the original list of Translated Names. We
+ // update each entry that has newly been translated by copying
+ // the entry from the new list and adjusting its Referenced
+ // Domain List Index upwards by adding the index of the first
+ // entry in the Next level List..
+ //
+
+ for( NextLevelSidIndex = 0;
+ NextLevelSidIndex < NextLevelCount;
+ NextLevelSidIndex++ ) {
+
+ if (!LsapDbCompletelyUnmappedName(&NextLevelNames[NextLevelSidIndex])) {
+
+ if (NextLevelNames[NextLevelSidIndex].Use != SidTypeUnknown) {
+
+ SidIndex = SidIndices[NextLevelSidIndex];
+
+ TranslatedNames->Names[SidIndex].Use
+ = NextLevelNames[NextLevelSidIndex].Use;
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ (PUNICODE_STRING) &TranslatedNames->Names[SidIndex].Name,
+ &NextLevelNames[NextLevelSidIndex].Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ TranslatedNames->Names[SidIndex].DomainIndex =
+ FirstEntryIndex +
+ NextLevelNames[NextLevelSidIndex].DomainIndex;
+
+ //
+ // Update the count of completely unmapped Sids.
+ //
+
+ (*CompletelyUnmappedCount)--;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInPrimaryDomainError;
+ }
+
+ //
+ // Update the Referenced Domain List if a new one was produced
+ // from the merge. We retain the original top-level structure.
+ //
+
+ if (OutputReferencedDomains != NULL) {
+
+ if (ReferencedDomains->Domains != NULL) {
+
+ MIDL_user_free( ReferencedDomains->Domains );
+ ReferencedDomains->Domains = NULL;
+ }
+
+ *ReferencedDomains = *OutputReferencedDomains;
+ MIDL_user_free( OutputReferencedDomains );
+ OutputReferencedDomains = NULL;
+ }
+
+ //
+ // Update the Mapped Count and close the Controller Policy
+ // Handle.
+ //
+
+ *MappedCount += NextLevelMappedCount;
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+
+LookupSidsInPrimaryDomainFinish:
+
+ //
+ // We can return partial translations for Sids that have the specified
+ // Domain Sid as Prefix Sid. We do this provided there has been
+ // no reported error. Errors resulting from callout to another
+ // LSA will have been suppressed.
+ //
+
+ if (NT_SUCCESS(Status) &&
+ (*MappedCount < Count) &&
+ !PartialSidTranslationsAttempted) {
+
+
+ SecondaryStatus = LsapDbLookupTranslateUnknownSidsInDomain(
+ Count,
+ Sids,
+ TrustInformation,
+ ReferencedDomains,
+ TranslatedNames,
+ LookupLevel,
+ MappedCount,
+ CompletelyUnmappedCount
+ );
+
+ PartialSidTranslationsAttempted = TRUE;
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ goto LookupSidsInPrimaryDomainError;
+ }
+ }
+
+ //
+ // If necessary, free the Next Level Referenced Domain List.
+ // Note that this structure is allocated(all_nodes) since it was
+ // allocated by the client side of the Domain Controller LSA.
+ //
+
+ if (NextLevelReferencedDomains != NULL) {
+
+ MIDL_user_free( NextLevelReferencedDomains );
+ NextLevelReferencedDomains = NULL;
+ }
+
+ //
+ // If necessary, free the Next Level Sids array. We only free the
+ // top level.
+ //
+
+ if (NextLevelSids != NULL) {
+
+ MIDL_user_free( NextLevelSids );
+ NextLevelSids = NULL;
+ }
+
+ //
+ // If necessary, free the Next Level Translated Names array.
+ // Note that this array is allocated(all_nodes).
+ //
+
+ if (NextLevelNames != NULL) {
+
+ MIDL_user_free( NextLevelNames );
+ NextLevelNames = NULL;
+ }
+
+ //
+ // If necessary, free the array that maps Sid Indices from the
+ // Next Level to the Current Level.
+ //
+
+ if (SidIndices != NULL) {
+
+ MIDL_user_free( SidIndices );
+ SidIndices = NULL;
+ }
+
+ //
+ // If necessary, close the Controller Policy Handle.
+ //
+
+ if ( ControllerPolicyHandle != NULL) {
+
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ goto LookupSidsInPrimaryDomainError;
+ }
+ }
+
+ return(Status);
+
+LookupSidsInPrimaryDomainError:
+
+ //
+ // If the primary status was a success code, but the secondary
+ // status was an error, propagate the secondary status.
+ //
+
+ if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto LookupSidsInPrimaryDomainFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupSidsInTrustedDomains(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to lookup Sids to see if they belong to
+ any of the Domains that are trusted by the Domain for which this
+ machine is a DC.
+
+Arguments:
+
+ Sids - Pointer to an array of Sids to be looked up.
+
+ Count - Number of Sids in the Sids array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ ReferencedDomains - Pointer to a Referenced Domain List structure.
+ The structure references an array of zero or more Trust Information
+ entries, one per referenced domain. This array will be appended to
+ or reallocated if necessary.
+
+ TranslatedNames - Pointer to structure that optionally references a list
+ of name translations for some of the Sids in the Sids array.
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids or Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ NOTE: LsapLookupWksta is not valid for this parameter.
+
+ MappedCount - Pointer to location containing the number of Sids
+ in the Sids array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Sids. A Sid is completely unmapped
+ if it is unknown and also its Domain Prefix Sid is not recognized.
+ This count is updated on exit, the number of completely unmapped
+ Sids whose Domain Prefices are identified by this routine being
+ subtracted from the input value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that
+ some or all of the Sids may remain unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
+
+ //
+ // Build a WorkList for this Lookup and put it on the Work Queue.
+ //
+ // NOTE: This routine does not need to hold the Lookup Work Queue
+ // lock to ensure validity of the WorkList pointer, because the
+ // pointer remains valid until this routine frees it via
+ // LsapDbLookupDeleteWorkList(). Although other threads may
+ // process the WorkList, do not delete it.
+ //
+ // A called routine must acquire the lock in order to access
+ // the WorkList after it has been added to the Work Queue.
+ //
+
+ Status = LsapDbLookupSidsBuildWorkList(
+ Count,
+ Sids,
+ ReferencedDomains,
+ TranslatedNames,
+ LookupLevel,
+ MappedCount,
+ CompletelyUnmappedCount,
+ &WorkList
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If no Work List has been built because there are no
+ // eligible domains to search, exit, suppressing the error.
+
+ if (Status == STATUS_NONE_MAPPED) {
+
+ Status = STATUS_SUCCESS;
+ goto LookupSidsInTrustedDomainsFinish;
+ }
+
+ goto LookupSidsInTrustedDomainsError;
+ }
+
+ //
+ // Start the work, by dispatching one or more worker threads
+ // if necessary.
+ //
+
+ Status = LsapDbLookupDispatchWorkerThreads( WorkList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInTrustedDomainsError;
+ }
+
+ //
+ // Wait for completion/termination of all items on the Work List.
+ //
+
+ Status = LsapDbLookupAwaitCompletionWorkList( WorkList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsInTrustedDomainsError;
+ }
+
+LookupSidsInTrustedDomainsFinish:
+
+ //
+ // If a Work List was created, delete it from the Work Queue
+ //
+
+ if (WorkList != NULL) {
+
+ Status = LsapDbLookupDeleteWorkList( WorkList );
+ WorkList = NULL;
+ }
+
+ return(Status);
+
+LookupSidsInTrustedDomainsError:
+
+ goto LookupSidsInTrustedDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupNamesInLocalDomains(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up Names in the local SAM domains and attempts to
+ translate them to Sids. Currently, there are two local SAM domains,
+ the Built-in domain (which has a well-known Sid and name) and the
+ Account Domain (which has a configurable Sid and name).
+
+Arguments:
+
+ Count - Number of Names in the Names array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Names - Pointer to array of Unicode Names to be translated.
+ Zero or all of the Names may already have been translated
+ elsewhere. If any of the Names have been translated, the
+ TranslatedSids parameter will point to a location containing a non-NULL
+ array of Sid translation structures corresponding to the
+ Names. If the nth Name has been translated, the nth Sid
+ translation structure will contain either a non-NULL RelativeId
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Name has not yet been translated, the nth Sid
+ translation structure will contain SidTypeUnknown in its
+ Use field.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ ReferencedDomains - Pointer to a location containing either NULL
+ or a pointer to a Referenced Domain List structure. If an
+ existing Referenced Domain List structure is supplied, it
+ will be appended/reallocated if necessary.
+
+ TranslatedSids - Pointer to structure that optionally references a list
+ of Sid translations for some of the Names in the Names array.
+
+ MappedCount - Pointer to location containing the number of Names
+ in the Names array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Sids. A Name is completely unmapped
+ if it is unknown and non-composite, or composite but with an
+ unrecognized Domain component. This count is updated on exit, the
+ number of completely unmapped Namess whose Domain Prefices are
+ identified by this routine being subtracted from the input value.
+
+ Options - Specifies optional actions.
+
+ LSAP_DB_SEARCH_BUILT_IN_DOMAIN - Search the Built In Domain
+
+ LSAP_DB_SEARCH_ACCOUNT_DOMAIN - Search the Account Domain
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Names may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INTERNAL_DB_ERROR - A corruption has been detected in
+ the LSA Database.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter
+
+ - No handle to the Policy object was provided on a request
+ to search the Account Domain.
+--*/
+
+{
+ NTSTATUS
+ Status,
+ SecondaryStatus = STATUS_SUCCESS;
+
+ ULONG
+ UpdatedMappedCount = *MappedCount;
+
+ LSAPR_HANDLE
+ TrustedPolicyHandle = NULL;
+
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO
+ PolicyAccountDomainInfo = NULL;
+
+
+ //
+ // If there are no completely unmapped Names remaining, return.
+ //
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupNamesInLocalDomainsFinish;
+ }
+
+
+ //
+ // If requested, lookup Names in the BUILT-IN Domain.
+ //
+
+ if (Options & LSAP_DB_SEARCH_BUILT_IN_DOMAIN) {
+
+ Status = LsapDbLookupNamesInLocalDomain(
+ LSAP_DB_SEARCH_BUILT_IN_DOMAIN,
+ Count,
+ PrefixNames,
+ SuffixNames,
+ BuiltInDomainTrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ &UpdatedMappedCount,
+ CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInLocalDomainsError;
+ }
+
+ //
+ // If all Names are now mapped or partially mapped, finish.
+ //
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupNamesInLocalDomainsFinish;
+ }
+ }
+
+ //
+ // If requested, search the Account Domain.
+ //
+
+ if (Options & LSAP_DB_SEARCH_ACCOUNT_DOMAIN) {
+
+ Status = LsapDbLookupNamesInLocalDomain(
+ LSAP_DB_SEARCH_ACCOUNT_DOMAIN,
+ Count,
+ PrefixNames,
+ SuffixNames,
+ AccountDomainTrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ &UpdatedMappedCount,
+ CompletelyUnmappedCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInLocalDomainsError;
+ }
+ }
+
+LookupNamesInLocalDomainsFinish:
+
+
+ //
+ // Return the updated total count of Names mapped.
+ //
+
+ *MappedCount = UpdatedMappedCount;
+ return(Status);
+
+LookupNamesInLocalDomainsError:
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto LookupNamesInLocalDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupNamesInLocalDomain(
+ IN ULONG LocalDomain,
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks up Names in a SAM domain on the local system and
+ attempts to translate them to Sids.
+
+Arguments:
+
+ LocalDomain - Indicates which local domain to look in. Valid values
+ are:
+ LSAP_DB_SEARCH_BUILT_IN_DOMAIN
+ LSAP_DB_SEARCH_ACCOUNT_DOMAIN
+
+ Count - Number of Names in the PrefixNames and SuffixNames arrays,
+ Note that some of these may already have been mapped elsewhere, as
+ specified by the MappedCount parameter.
+
+ PrefixNames - Pointer to array of Prefix Names. These are Domain
+ Names or NULL Unicode Strings. Only those Names whose Prefix
+ Names are NULL or the same as the Domain Name specified in the
+ TrustInformation parameter are eligible for the search.
+
+ SuffixNames - Pointer to array of Terminal Names to be translated.
+ Terminal Names are the last component of the name.
+ Zero or all of the Names may already have been translated
+ elsewhere. If any of the Names have been translated, the
+ Sids parameter will point to a location containing a non-NULL
+ array of Sid translation structures corresponding to the
+ Sids. If the nth Sid has been translated, the nth Sid
+ translation structure will contain either a non-NULL Sid
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Sid has not yet been translated, the nth name
+ translation structure will contain a zero-length name string
+ and a negative value for the Referenced Domain List index.
+
+ TrustInformation - Pointer to Trust Information specifying a Domain Sid
+ and Name.
+
+ ReferencedDomains - Pointer to a Referenced Domain List. This
+ list references an array of zero or more Trust Information
+ entries describing each of the domains referenced by the names.
+ This array will be appended to/reallocated if necessary.
+
+ TranslatedSids - Pointer to location containing NULL or a pointer to a
+ array of Sid translation structures.
+
+ MappedCount - Pointer to location containing the number of Names
+ in the Names array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Sids. A Name is completely unmapped
+ if it is unknown and non-composite, or composite but with an
+ unrecognized Domain component. This count is updated on exit, the
+ number of completely unmapped Namess whose Domain Prefices are
+ identified by this routine being subtracted from the input value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Names may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS
+ Status = STATUS_SUCCESS,
+ SecondaryStatus = STATUS_SUCCESS,
+ IgnoreStatus;
+
+ ULONG
+ UnmappedNameCount = 0,
+ OutputSidsLength,
+ NameLookupCount,
+ NameLookupIndex,
+ SidIndex;
+
+ LONG
+ DomainIndex = LSA_UNKNOWN_INDEX;
+
+ PULONG
+ SidIndices = NULL;
+
+ PLSA_TRANSLATED_SID
+ OutputSids = NULL;
+
+ SAMPR_HANDLE
+ LocalSamDomainHandle = NULL,
+ LocalSamUserHandle = NULL;
+
+ PLSAPR_UNICODE_STRING
+ SamLookupSuffixNames = NULL,
+ DomainName = &TrustInformation->Name;
+
+ PLSAPR_SID
+ DomainSid = TrustInformation->Sid;
+
+ SAMPR_ULONG_ARRAY
+ SamReturnedIds,
+ SamReturnedUses;
+
+ PLSAPR_TRUST_INFORMATION
+ FreeTrustInformation = NULL;
+
+ PUSER_CONTROL_INFORMATION
+ UserControlInfo;
+
+ SamReturnedIds.Count = 0;
+ SamReturnedIds.Element = NULL;
+ SamReturnedUses.Count = 0;
+ SamReturnedUses.Element = NULL;
+
+
+ //
+ // Make sure the SAM handles have been established.
+ //
+
+ Status = LsapOpenSam();
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // It is an internal error if the TranslatedSids or ReferencedDomains
+ // parameters have not been specified. Further, The TranslatedSids->Sids
+ // pointer must be non-NULL.
+ //
+
+ ASSERT(ARGUMENT_PRESENT(TranslatedSids));
+ ASSERT(TranslatedSids->Sids != NULL);
+ ASSERT(ARGUMENT_PRESENT(ReferencedDomains));
+
+ //
+ // Validate the Count and MappedCount parameters.
+ //
+
+
+ if (*MappedCount + *CompletelyUnmappedCount > Count) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto LookupNamesInLocalDomainError;
+ }
+
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupNamesInLocalDomainFinish;
+ }
+
+ //
+ // Not all of the Names have yet been mapped. We'll try to map the
+ // remaining names by searching the designated SAM domain on this
+ // machine.
+ //
+
+ UnmappedNameCount = Count - *MappedCount;
+ OutputSids = (PLSA_TRANSLATED_SID) TranslatedSids->Sids;
+ OutputSidsLength = Count * sizeof (LSA_TRANSLATED_SID);
+
+ //
+ // Allocate memory for array of names to be presented to SAM. Note
+ // that, for simplicity, we allocate an array for the maximal case
+ // in which all of the reamining unmapped names are eligible
+ // for the search in this domain. This avoids having to scan the
+ // names array twice, once to compute the number of eligible names.
+ //
+
+ SamLookupSuffixNames = LsapAllocateLsaHeap( UnmappedNameCount * sizeof(UNICODE_STRING));
+
+
+ if (SamLookupSuffixNames == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto LookupNamesInLocalDomainError;
+ }
+
+ //
+ // Allocate an array to record indices of eligible names. This is
+ // used upon return from SAM to locate the entries in the
+ // OutputSids array that are to be updated. For simplicity, we
+ // allocate the array with sufficient entries for all of the remaining
+ // unmapped names.
+ //
+
+ SidIndices = LsapAllocateLsaHeap( UnmappedNameCount * sizeof(ULONG));
+
+ if (SidIndices == NULL) {
+
+ goto LookupNamesInLocalDomainError;
+ }
+
+
+ //
+ // Scan the output array of Sid translations to locate entries for names
+ // that have not yet been mapped. For each unmapped name, check
+ // eligibility of the name for the search of this domain.
+ //
+ // - All isolated names are eligible for the search
+ // - All composite names having this domain name as prefix are
+ // eligible.
+ //
+ // Copy in each eligible suffix or isolated name to the SAM buffer.
+ //
+
+ for (NameLookupIndex = 0, SidIndex = 0; SidIndex < Count; SidIndex++) {
+
+ if (OutputSids[SidIndex].Use == SidTypeUnknown) {
+
+ //
+ // Found a name that has not yet been mapped. Check for a name
+ // Prefix. If none has been specified, the whole name may either
+ // be NULL, the name of the domain itself or an isolated name.
+ //
+
+ if (PrefixNames[SidIndex].Length == 0) {
+
+ //
+ // Name is isolated. If whole name is NULL, skip.
+ //
+
+ if (SuffixNames[SidIndex].Length == 0) {
+
+ continue;
+ }
+
+ //
+ // If name is the name of the domain itself, use the
+ // Trust Information to translate it, and fill in the
+ // appropriate Translated Sids entry. The name will
+ // then be excluded from further searches.
+ //
+
+ if (RtlEqualDomainName(
+ (PUNICODE_STRING) &(TrustInformation->Name),
+ (PUNICODE_STRING) &SuffixNames[SidIndex]
+ )) {
+
+
+ Status = LsapDbLookupTranslateNameDomain(
+ TrustInformation,
+ &OutputSids[SidIndex],
+ ReferencedDomains,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ continue;
+ }
+
+ //
+ // Name is an isolated name not equal to the domain name and
+ // so is eligible for the search. Reference it from the SAM buffer,
+ // remember its index and increment the buffer index. The
+ // SAM buffer is an IN parameter to a further lookup call.
+ //
+
+ SamLookupSuffixNames[NameLookupIndex] = SuffixNames[SidIndex];
+
+
+ //
+ // We should never have an index that equals or exceeds the
+ // UnmappedNameCount.
+ //
+
+ ASSERT(NameLookupIndex < UnmappedNameCount);
+
+ SidIndices[NameLookupIndex] = SidIndex;
+ NameLookupIndex++;
+ continue;
+ }
+
+ //
+ // Name has a non-NULL Prefix Name. Cpmpare the name with the
+ // name of the Domain being searched.
+
+ if (RtlEqualDomainName(
+ (PUNICODE_STRING) &TrustInformation->Name,
+ (PUNICODE_STRING) &PrefixNames[SidIndex]
+ )) {
+
+ //
+ // Prefix name matches the name of the Domain. If the
+ // Suffix Name is NULL, the name of the domain itself
+ // has been specified (in the form <DomainName> "\").
+ //
+
+ if (SuffixNames[SidIndex].Length == 0) {
+
+ Status = LsapDbLookupTranslateNameDomain(
+ TrustInformation,
+ &OutputSids[SidIndex],
+ ReferencedDomains,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ continue;
+ }
+
+ //
+ // Name is composite and the Prefix name matches the name of
+ // this domain. We will at least be able to partially translate
+ // name, so add this domain to the Referenced Domain List if not
+ // already there. Then reference the Suffix Name from the SAM buffer,
+ // remember its index and increment the buffer index. The
+ // SAM buffer is an IN parameter to a further lookup call.
+ //
+
+ if (DomainIndex == LSA_UNKNOWN_INDEX) {
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ SamLookupSuffixNames[NameLookupIndex] = SuffixNames[SidIndex];
+
+ SidIndices[NameLookupIndex] = SidIndex;
+ OutputSids[SidIndex].DomainIndex = DomainIndex;
+ NameLookupIndex++;
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInLocalDomainError;
+ }
+
+ //
+ // If none of the remaining Names are eligible for searching in this
+ // domain, finish up.
+ //
+
+ NameLookupCount = NameLookupIndex;
+
+ if (NameLookupCount == 0) {
+
+ goto LookupNamesInLocalDomainFinish;
+ }
+
+ //
+ //
+ // Lookup the Sids in the specified SAM Domain.
+ //
+
+ if (LocalDomain == LSAP_DB_SEARCH_BUILT_IN_DOMAIN ) {
+ LocalSamDomainHandle = LsapBuiltinDomainHandle;
+ } else {
+ ASSERT(LocalDomain == LSAP_DB_SEARCH_ACCOUNT_DOMAIN);
+ LocalSamDomainHandle = LsapAccountDomainHandle;
+ }
+
+ //
+ // Call SAM to lookup the Names in the Domain.
+ //
+
+ Status = SamrLookupNamesInDomain(
+ LocalSamDomainHandle,
+ NameLookupCount,
+ (PRPC_UNICODE_STRING) SamLookupSuffixNames,
+ &SamReturnedIds,
+ &SamReturnedUses
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // The only error allowed is STATUS_NONE_MAPPED. Filter this out.
+ //
+
+ if (Status != STATUS_NONE_MAPPED) {
+
+ goto LookupNamesInLocalDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+ goto LookupNamesInLocalDomainFinish;
+ }
+
+ //
+ // Filter through the returned Ids to eliminate user accounts that are
+ // not marked NORMAL.
+ //
+
+ for (NameLookupIndex = 0;
+ NameLookupIndex < SamReturnedIds.Count;
+ NameLookupIndex++) {
+
+ //
+ // If this account is a User Account, check its User Control
+ // Information. If the account control information indicates
+ // that the account is not a Normal User Account, for example
+ // it is a machine account, do not return information about
+ // the account. Mark it as unknown.
+ //
+
+ if (SamReturnedUses.Element[ NameLookupIndex ] != SidTypeUser) {
+
+ continue;
+ }
+
+ Status = SamrOpenUser(
+ LocalSamDomainHandle,
+ USER_READ_ACCOUNT,
+ SamReturnedIds.Element[ NameLookupIndex ],
+ &LocalSamUserHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ UserControlInfo = NULL;
+
+ Status = SamrQueryInformationUser(
+ LocalSamUserHandle,
+ UserControlInformation,
+ (PSAMPR_USER_INFO_BUFFER *) &UserControlInfo
+ );
+ IgnoreStatus = SamrCloseHandle(&LocalSamUserHandle);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ LocalSamUserHandle = NULL;
+
+
+ if (!NT_SUCCESS(Status)) {
+ MIDL_user_free( UserControlInfo );
+ break;
+ }
+
+ if (!(UserControlInfo->UserAccountControl & USER_NORMAL_ACCOUNT) &&
+ !(UserControlInfo->UserAccountControl & USER_TEMP_DUPLICATE_ACCOUNT)) {
+ SamReturnedUses.Element[NameLookupIndex] = SidTypeUnknown;
+ }
+
+ MIDL_user_free( UserControlInfo );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInLocalDomainError;
+ }
+
+ //
+ // SAM found at least one eligible name in the specified domain.
+ // Add the domain to the Referenced Domain List.
+ //
+
+ Status = LsapDbLookupTranslateNameDomain(
+ TrustInformation,
+ NULL,
+ ReferencedDomains,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInLocalDomainError;
+ }
+
+ //
+ // Now copy the information returned from SAM into the output
+ // Translated Sids array.
+ //
+
+ for (NameLookupIndex = 0;
+ NameLookupIndex < SamReturnedIds.Count;
+ NameLookupIndex++) {
+
+ SidIndex = SidIndices[NameLookupIndex];
+
+ //
+ // If we have newly translated a Name, increment the mapped
+ // count and copy information returned by SAM.
+ //
+
+ if ((OutputSids[SidIndex].Use == SidTypeUnknown) &&
+ SamReturnedUses.Element[NameLookupIndex] != (ULONG) SidTypeUnknown) {
+
+ (*MappedCount)++;
+ OutputSids[SidIndex].Use = (SID_NAME_USE) SamReturnedUses.Element[NameLookupIndex];
+ OutputSids[SidIndex].RelativeId = SamReturnedIds.Element[NameLookupIndex];
+ OutputSids[SidIndex].DomainIndex = DomainIndex;
+ }
+ }
+
+LookupNamesInLocalDomainFinish:
+
+ //
+ // If successful, update count of completely unmapped names provided.
+ // Note that STATUS_NONE_MAPPED errors are suppressed before we get here.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ LsapDbUpdateCountCompUnmappedNames(TranslatedSids, CompletelyUnmappedCount);
+ }
+
+ //
+ // If necessary, free the Lsa Heap buffer allocated for the
+ // SamLookupSuffixNames and SidIndices arrays.
+ //
+
+ if (SamLookupSuffixNames != NULL) {
+ LsapFreeLsaHeap( SamLookupSuffixNames );
+ }
+
+ if (SidIndices != NULL) {
+ LsapFreeLsaHeap( SidIndices );
+ }
+
+ //
+ // If necessary, free the Relative Ids array returned from SAM.
+ //
+
+ if ( SamReturnedIds.Count != 0 ) {
+ SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedIds );
+ }
+
+ //
+ // If necessary, free the Uses array returned from SAM.
+ //
+
+ if ( SamReturnedUses.Count != 0 ) {
+ SamIFree_SAMPR_ULONG_ARRAY ( &SamReturnedUses );
+ }
+
+
+ return(Status);
+
+LookupNamesInLocalDomainError:
+
+ //
+ // If necessary, free memory for the OutputTrustInformation Domain
+ // Name Buffer and Sid Buffer.
+ //
+
+ if (DomainIndex >= 0) {
+
+ FreeTrustInformation = &ReferencedDomains->Domains[DomainIndex];
+
+ if (FreeTrustInformation->Sid != NULL) {
+
+ MIDL_user_free( FreeTrustInformation->Sid );
+ FreeTrustInformation->Sid = NULL;
+ }
+
+ if (FreeTrustInformation->Name.Buffer != NULL) {
+
+ MIDL_user_free( FreeTrustInformation->Name.Buffer );
+ FreeTrustInformation->Name.Buffer = NULL;
+ FreeTrustInformation->Name.Length = 0;
+ FreeTrustInformation->Name.MaximumLength = 0;
+ }
+ }
+
+ //
+ // Reset the Use field for each of the entries written to back to
+ // SidTypeUnknown and set the DomainIndex back to LSA_UNKNOWN_INDEX.
+ //
+
+ for (NameLookupIndex = 0;
+ NameLookupIndex < SamReturnedIds.Count;
+ NameLookupIndex++) {
+
+ SidIndex = SidIndices[NameLookupIndex];
+ OutputSids[SidIndex].Use = SidTypeUnknown;
+ OutputSids[SidIndex].DomainIndex = LSA_UNKNOWN_INDEX;
+ }
+
+
+ //
+ // If the last User Account handle is still open, close it..
+ //
+
+ if (LocalSamUserHandle != NULL) {
+ SecondaryStatus = SamrCloseHandle(&LocalSamUserHandle);
+ LocalSamUserHandle = NULL;
+ }
+
+ goto LookupNamesInLocalDomainFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupNamesInPrimaryDomain(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to translate Names by searching the Primary
+ Domain.
+
+Arguments:
+
+ Count - Number of Names in the Names array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Names - Pointer to array of Names to be translated.
+ Zero or all of the Names may already have been translated
+ elsewhere. If any of the Names have been translated, the
+ TranslatedSids parameter will point to a location containing a non-NULL
+ array of Sid translation structures corresponding to the
+ Names. If the nth Name has been translated, the nth Sid
+ translation structure will contain either a non-NULL Sid
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Name has not yet been translated, the nth Sid
+ translation structure will contain a zero-length Sid string
+ and a negative value for the Referenced Domain List index.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ TrustInformation - Specifies the name and Sid of the Primary Domain.
+
+ ReferencedDomains - Pointer to a Referenced Domain List. This
+ list references an array of zero or more Trust Information
+ entries describing each of the domains referenced by the names.
+ This array will be appended to/reallocated if necessary.
+
+ TranslatedSids - Pointer to structure that optionally references a list
+ of Sid translations for some of the Names in the Names array.
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ NOTE: LsapLookupWksta is not valid for this parameter.
+
+ MappedCount - Pointer to location containing the number of Names
+ in the Names array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Names. A Name is completely unmapped
+ if it is unknown and non-composite, or composite but with an
+ unrecognized Domain component. This count is updated on exit, the
+ number of completely unmapped Namess whose Domain Prefices are
+ identified by this routine being subtracted from the input value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Names may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ LSA_HANDLE ControllerPolicyHandle = NULL;
+ ULONG NextLevelCount;
+ ULONG NextLevelMappedCount;
+ ULONG NameIndex;
+ ULONG NextLevelNameIndex;
+ PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
+ PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
+ PLSA_TRANSLATED_SID NextLevelSids = NULL;
+ PLSAPR_UNICODE_STRING NextLevelNames = NULL;
+ LONG FirstEntryIndex;
+ PULONG NameIndices = NULL;
+ BOOLEAN PartialNameTranslationsAttempted = FALSE;
+
+ //
+ // If there are no completely unmapped Names remaining, just return.
+ //
+
+ if (*CompletelyUnmappedCount == (ULONG) 0) {
+
+ goto LookupNamesInPrimaryDomainFinish;
+ }
+
+ //
+ // Open the Policy object on some DC for the Primary Domain.
+ //
+
+ Status = LsapDbOpenPolicyTrustedDomain(
+ TrustInformation,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &ControllerPolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // We cannot access the Primary Domain. Suppress the error
+ // and translate Domain Prefix Sids for Sids belonging to
+ // the Primary Domain.
+ //
+
+ Status = STATUS_SUCCESS;
+ goto LookupNamesInPrimaryDomainFinish;
+ }
+
+ //
+ // We have successfully opened a Domain Controller's Policy
+ // Database. Now prepare to hand off a Name lookup for the
+ // remaining unmapped Names to that Controller. Here, this
+ // server side of the LSA is a client of the LSA on the
+ // target controller. We will construct an array of the
+ // remianing unmapped Names, look them up and then merge the
+ // resulting ReferencedDomains and Translated Sids into
+ // our existing list.
+ //
+
+ NextLevelCount = *CompletelyUnmappedCount;
+
+ //
+ // Allocate an array to hold the indices of unmapped Names
+ // relative to the original Names and TranslatedSids->Sids
+ // arrays.
+ //
+
+ NameIndices = MIDL_user_allocate(NextLevelCount * sizeof(ULONG));
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (NameIndices == NULL) {
+
+ goto LookupNamesInPrimaryDomainError;
+ }
+
+ //
+ // Allocate an array of UNICODE_STRING structures for the
+ // names to be looked up at the Domain Controller.
+ //
+
+ NextLevelNames = MIDL_user_allocate(
+ sizeof(UNICODE_STRING) * NextLevelCount
+ );
+
+ if (NextLevelNames == NULL) {
+
+ goto LookupNamesInPrimaryDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Now scan the original array of Names and its parallel
+ // Translated Sids array. Copy over any names that are completely
+ // unmapped.
+ //
+
+ NextLevelNameIndex = (ULONG) 0;
+
+ for (NameIndex = 0;
+ NameIndex < Count && NextLevelNameIndex < NextLevelCount;
+ NameIndex++) {
+
+ if (LsapDbCompletelyUnmappedSid(&TranslatedSids->Sids[NameIndex])) {
+
+ NextLevelNames[NextLevelNameIndex] = Names[NameIndex];
+ NameIndices[NextLevelNameIndex] = NameIndex;
+ NextLevelNameIndex++;
+ }
+ }
+
+ NextLevelMappedCount = (ULONG) 0;
+
+ Status = LsaICLookupNames(
+ ControllerPolicyHandle,
+ NextLevelCount,
+ (PUNICODE_STRING) NextLevelNames,
+ (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
+ (PLSA_TRANSLATED_SID *) &NextLevelSids,
+ LookupLevel,
+ &NextLevelMappedCount
+ );
+
+ //
+ // If the callout to LsaLookupNames() was unsuccessful, disregard
+ // the error and set the domain name for any Sids having this
+ // domain Sid as prefix sid.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_SUCCESS;
+ goto LookupNamesInPrimaryDomainFinish;
+ }
+
+ //
+ // The callout to LsaLookupNames() was successful. We now have
+ // an additional list of Referenced Domains containing the
+ // Primary Domain and/or one or more of its Trusted Domains.
+ // Merge the two Referenced Domain Lists together, noting that
+ // since they are disjoint, the second list is simply
+ // concatenated with the first. The index of the first entry
+ // of the second list will be used to adjust all of the
+ // Domain Index entries in the Translated Names entries.
+ // Note that since the memory for the graph of the first
+ // Referenced Domain list has been allocated as individual
+ // nodes, we specify that the nodes in this graph can be
+ // referenced by the output Referenced Domain list.
+ //
+
+ Status = LsapDbLookupMergeDisjointReferencedDomains(
+ ReferencedDomains,
+ NextLevelReferencedDomains,
+ &OutputReferencedDomains,
+ LSAP_DB_USE_FIRST_MERGAND_GRAPH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInPrimaryDomainError;
+ }
+
+ FirstEntryIndex = ReferencedDomains->Entries;
+
+ //
+ // Now update the original list of Translated Names. We
+ // update each entry that has newly been translated by copying
+ // the entry from the new list and adjusting its
+ // Referenced Domain List Index upwards by adding the index
+ // of the first entry in the Next level List..
+ //
+
+ for( NextLevelNameIndex = 0;
+ NextLevelNameIndex < NextLevelCount;
+ NextLevelNameIndex++ ) {
+
+ if (!LsapDbCompletelyUnmappedSid(&NextLevelSids[NextLevelNameIndex])) {
+
+ NameIndex = NameIndices[NextLevelNameIndex];
+
+ TranslatedSids->Sids[NameIndex]
+ = NextLevelSids[NextLevelNameIndex];
+
+ TranslatedSids->Sids[NameIndex].DomainIndex =
+ FirstEntryIndex +
+ NextLevelSids[NextLevelNameIndex].DomainIndex;
+
+ (*CompletelyUnmappedCount)--;
+ }
+ }
+
+ //
+ // Update the Referenced Domain List if a new one was produced
+ // from the merge. We retain the original top-level structure.
+ //
+
+ if (OutputReferencedDomains != NULL) {
+
+ if (ReferencedDomains->Domains != NULL) {
+
+ MIDL_user_free( ReferencedDomains->Domains );
+ ReferencedDomains->Domains = NULL;
+ }
+
+ *ReferencedDomains = *OutputReferencedDomains;
+ MIDL_user_free( OutputReferencedDomains );
+ OutputReferencedDomains = NULL;
+ }
+
+ //
+ // Update the Mapped Count and close the Controller Policy
+ // Handle.
+ //
+
+ *MappedCount += NextLevelMappedCount;
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+
+ //
+ // Any error status that has not been suppressed must be reported
+ // to the caller. Errors such as connection failures to other LSA's
+ // are suppressed.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInPrimaryDomainError;
+ }
+
+LookupNamesInPrimaryDomainFinish:
+
+ //
+ // If necessary, update count of completely unmapped names.
+ //
+
+ if (*CompletelyUnmappedCount > (ULONG) 0) {
+
+ LsapDbUpdateCountCompUnmappedNames(TranslatedSids, CompletelyUnmappedCount);
+ }
+
+ //
+ // We can return partial translations for composite Names in which the
+ // domain component is known. We do this provided there has been
+ // no reported error. Errors resulting from callout to another
+ // LSA will have been suppressed.
+ //
+
+ if (NT_SUCCESS(Status) &&
+ (*MappedCount < Count) &&
+ !PartialNameTranslationsAttempted) {
+
+ SecondaryStatus = LsapDbLookupTranslateUnknownNamesInDomain(
+ Count,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ TrustInformation,
+ ReferencedDomains,
+ TranslatedSids,
+ LookupLevel,
+ MappedCount,
+ CompletelyUnmappedCount
+ );
+
+ PartialNameTranslationsAttempted = TRUE;
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ goto LookupNamesInPrimaryDomainError;
+ }
+ }
+
+ //
+ // If necessary, free the Next Level Referenced Domain List.
+ // Note that this structure is allocated(all_nodes) since it was
+ // allocated by the client side of the Domain Controller LSA.
+ //
+
+ if (NextLevelReferencedDomains != NULL) {
+
+ MIDL_user_free( NextLevelReferencedDomains );
+ NextLevelReferencedDomains = NULL;
+ }
+
+ //
+ // If necessary, free the Next Level Names array. We only free the
+ // top level, since the names therein were copied from the input
+ // TranslatedNames->Names array.
+ //
+
+ if (NextLevelNames != NULL) {
+
+ MIDL_user_free( NextLevelNames );
+ NextLevelNames = NULL;
+ }
+
+ //
+ // If necessary, free the Next Level Translated Sids array. Note
+ // that this array is allocated(all_nodes).
+ //
+
+ if (NextLevelSids != NULL) {
+
+ MIDL_user_free( NextLevelSids );
+ NextLevelSids = NULL;
+ }
+
+ //
+ // If necessary, free the array that maps Name Indices from the
+ // Next Level to the Current Level.
+ //
+
+ if (NameIndices != NULL) {
+
+ MIDL_user_free( NameIndices );
+ NameIndices = NULL;
+ }
+
+ //
+ // If necessary, close the Controller Policy Handle.
+ //
+
+ if ( ControllerPolicyHandle != NULL) {
+
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ goto LookupNamesInPrimaryDomainError;
+ }
+ }
+
+ return(Status);
+
+LookupNamesInPrimaryDomainError:
+
+ //
+ // If the primary status was a success code, but the secondary
+ // status was an error, propagate the secondary status.
+ //
+
+ if ((!NT_SUCCESS(SecondaryStatus)) && NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto LookupNamesInPrimaryDomainFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupNamesInTrustedDomains(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to lookup Names to see if they belong to
+ any of the Domains that are trusted by the Domain for which this
+ machine is a DC.
+
+Arguments:
+
+ Count - Number of Names in the Names array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Names - Pointer to array of Names to be translated. Zero or all of the
+ Names may already have been translated elsewhere. If any of the
+ Names have been translated, the TranslatedSids parameter will point
+ to a location containing a non-NULL array of Sid translation
+ structures corresponding to the Names. If the nth Name has been
+ translated, the nth Sid translation structure will contain either a
+ non-NULL Sid or a non-negative offset into the Referenced Domain
+ List. If the nth Name has not yet been translated, the nth Sid
+ translation structure will contain a zero-length Sid string and a
+ negative value for the Referenced Domain List index.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ ReferencedDomains - Pointer to a Referenced Domain List. This
+ list references an array of zero or more Trust Information
+ entries describing each of the domains referenced by the names.
+ This array will be appended to/reallocated if necessary.
+
+ TranslatedSids - Pointer to structure that optionally references a list
+ of Sid translations for some of the Names in the Names array.
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ NOTE: LsapLookupWksta is not valid for this parameter.
+
+ MappedCount - Pointer to location containing the number of Names
+ in the Names array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Names. A Name is completely unmapped
+ if it is unknown and non-composite, or composite but with an
+ unrecognized Domain component. This count is updated on exit, the
+ number of completely unmapped Namess whose Domain Prefices are
+ identified by this routine being subtracted from the input value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Names may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
+
+ //
+ // Build a WorkList for this Lookup and put it on the Work Queue.
+ //
+ // NOTE: This routine does not need to hold the Lookup Work Queue
+ // lock to ensure validity of the WorkList pointer, because the
+ // pointer remains valid until this routine frees it via
+ // LsapDbLookupDeleteWorkList(). Although other threads may
+ // process the WorkList, do not delete it.
+ //
+ // A called routine must acquire the lock in order to access
+ // the WorkList after it has been added to the Work Queue.
+ //
+
+ Status = LsapDbLookupNamesBuildWorkList(
+ Count,
+ Names,
+ PrefixNames,
+ SuffixNames,
+ ReferencedDomains,
+ TranslatedSids,
+ LookupLevel,
+ MappedCount,
+ CompletelyUnmappedCount,
+ &WorkList
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If no Work List has been built because there are no
+ // eligible domains to search, exit, suppressing the error.
+
+ if (Status == STATUS_NONE_MAPPED) {
+
+ Status = STATUS_SUCCESS;
+ goto LookupNamesInTrustedDomainsFinish;
+ }
+
+ goto LookupNamesInTrustedDomainsError;
+ }
+
+ //
+ // Start the work, by dispatching one or more worker threads
+ // if necessary.
+ //
+
+ Status = LsapDbLookupDispatchWorkerThreads( WorkList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInTrustedDomainsError;
+ }
+
+ //
+ // Wait for completion/termination of all items on the Work List.
+ //
+
+ Status = LsapDbLookupAwaitCompletionWorkList( WorkList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesInTrustedDomainsError;
+ }
+
+LookupNamesInTrustedDomainsFinish:
+
+ //
+ // If a Work List was created, delete it from the Work Queue
+ //
+
+ if (WorkList != NULL) {
+
+ Status = LsapDbLookupDeleteWorkList( WorkList );
+ WorkList = NULL;
+ }
+
+ return(Status);
+
+LookupNamesInTrustedDomainsError:
+
+ goto LookupNamesInTrustedDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupTranslateNameDomain(
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OPTIONAL PLSA_TRANSLATED_SID TranslatedSid,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ OUT PLONG DomainIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This function optionally produces a Translated Sid entry for Domain
+ from its Name and Sid, and adds Trust Information for the Domain to
+ a Referenced Domain List. The index of the new (or existing) entry
+ in the Referenced Domain List is returned.
+
+Arguments:
+
+ TrustInformation - Pointer to Trust Information for the Domain consisting
+ of its Name and Sid.
+
+ TranslatedSid - Optional pointer to Translated Sid entry which will
+ be filled in with the translation for this Domain. If NULL
+ is specified, no entry is fiiled in.
+
+ ReferencedDomains - Pointer to Referenced Domain List in which an
+ entry consisting of the Trust Information for the Domain will be
+ made if one does not already exist.
+
+ DomainIndex - Receives the index of the existing or new entry for the
+ Domain in the Referenced Domain List.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto TranslateNameDomainError;
+ }
+
+ //
+ // If requested, fill in a Sid translation entry for the domain.
+ //
+
+ if (TranslatedSid != NULL) {
+
+ TranslatedSid->Use = SidTypeDomain;
+ TranslatedSid->RelativeId = LSA_UNKNOWN_ID;
+ TranslatedSid->DomainIndex = *DomainIndex;
+ }
+
+TranslateNameDomainFinish:
+
+ return(Status);
+
+TranslateNameDomainError:
+
+ goto TranslateNameDomainFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupTranslateUnknownSidsInDomain(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks among the unknown Sids in the given list and
+ translates the Domain Name for any whose Domain Prefix Sid matches
+ the given Domain Sid.
+
+Arguments:
+
+ Count - Number of Sids in the Sids array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Sids - Pointer to array of pointers to Sids to be translated.
+ Zero or all of the Sids may already have been translated
+ elsewhere. If any of the Sids have been translated, the
+ Names parameter will point to a location containing a non-NULL
+ array of Name translation structures corresponding to the
+ Sids. If the nth Sid has been translated, the nth name
+ translation structure will contain either a non-NULL name
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Sid has not yet been translated, the nth name
+ translation structure will contain a zero-length name string
+ and a negative value for the Referenced Domain List index.
+
+ TrustInformation - Pointer to Trust Information specifying a Domain Sid
+ and Name.
+
+ ReferencedDomains - Pointer to a Referenced Domain List structure.
+ The structure references an array of zero or more Trust Information
+ entries, one per referenced domain. This array will be appended to
+ or reallocated if necessary.
+
+ TranslatedNames - Pointer to structure that optionally references a list
+ of name translations for some of the Sids in the Sids array.
+
+ MappedCount - Pointer to location containing the number of Sids
+ in the Sids array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Sids. A Sid is completely unmapped
+ if it is unknown and also its Domain Prefix Sid is not recognized.
+ This count is updated on exit, the number of completely unmapped
+ Sids whose Domain Prefices are identified by this routine being
+ subtracted from the input value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Sids may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG RemainingCompletelyUnmappedCount;
+ ULONG SidIndex;
+ PSID DomainSid = TrustInformation->Sid;
+ BOOLEAN DomainAlreadyAdded = FALSE;
+ LONG DomainIndex;
+
+ //
+ // Scan the array of Sids looking for ones whose domain has not been
+ // found.
+ //
+
+ for( SidIndex = 0,
+ RemainingCompletelyUnmappedCount = *CompletelyUnmappedCount;
+ (RemainingCompletelyUnmappedCount > 0) && (SidIndex < Count);
+ SidIndex++) {
+
+ //
+ // Check if this Sid is completely unmapped (i.e. its domain
+ // has not yet been identified.
+ //
+
+ if (LsapDbCompletelyUnmappedName(&TranslatedNames->Names[SidIndex])) {
+
+ //
+ // Found a completely unmapped Sid. If it belongs to the
+ // specified Domain, add the Domain to the Referenced Domain
+ // list if we have not already done so.
+ //
+
+ if (LsapRtlPrefixSid( DomainSid, (PSID) Sids[SidIndex])) {
+
+ if (!DomainAlreadyAdded) {
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ DomainAlreadyAdded = TRUE;
+ }
+
+ //
+ // Reference the domain from the TranslatedNames entry
+ //
+
+ TranslatedNames->Names[SidIndex].DomainIndex = DomainIndex;
+
+ //
+ // This Sid is now partially translated, so reduce the
+ // count of completely unmapped Sids.
+ //
+
+ (*CompletelyUnmappedCount)--;
+ }
+
+ //
+ // Decrement count of completely unmapped Sids scanned.
+ //
+
+ RemainingCompletelyUnmappedCount--;
+ }
+ }
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+LsapDbLookupTranslateUnknownNamesInDomain(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks among the unknown Sids in the given list and
+ translates the Domain Name for any whose Domain Prefix Sid matches
+ the given Domain Sid.
+
+Arguments:
+
+ Count - Number of Names in the Names array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Names - Pointer to array of Names to be translated.
+ Zero or all of the Names may already have been translated
+ elsewhere. If any of the Names have been translated, the
+ TranslatedSids parameter will point to a location containing a non-NULL
+ array of Sid translation structures corresponding to the
+ Names. If the nth Name has been translated, the nth Sid
+ translation structure will contain either a non-NULL Sid
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Name has not yet been translated, the nth Sid
+ translation structure will contain a zero-length Sid string
+ and a negative value for the Referenced Domain List index.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ TrustInformation - Pointer to Trust Information specifying a Domain Sid
+ and Name.
+
+ ReferencedDomains - Pointer to a Referenced Domain List. This
+ list references an array of zero or more Trust Information
+ entries describing each of the domains referenced by the names.
+ This array will be appended to/reallocated if necessary.
+
+ TranslatedSids - Pointer to structure that optionally references a list
+ of Sid translations for some of the Names in the Names array.
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ NOTE: LsapLookupWksta is not valid for this parameter.
+
+ MappedCount - Pointer to location containing the number of Names
+ in the Names array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Names. A Name is completely unmapped
+ if it is unknown and non-composite, or composite but with an
+ unrecognized Domain component. This count is updated on exit, the
+ number of completely unmapped Namess whose Domain Prefices are
+ identified by this routine being subtracted from the input value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully. Note that some
+ or all of the Names may remain partially or completely unmapped.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG RemainingCompletelyUnmappedCount;
+ ULONG NameIndex;
+ PLSAPR_UNICODE_STRING DomainName = &TrustInformation->Name;
+ BOOLEAN DomainAlreadyAdded = FALSE;
+ LONG DomainIndex;
+
+ //
+ // Scan the array of Names looking for composite ones whose domain has
+ // not been found.
+ //
+
+ for( NameIndex = 0,
+ RemainingCompletelyUnmappedCount = *CompletelyUnmappedCount;
+ (RemainingCompletelyUnmappedCount > 0) && (NameIndex < Count);
+ NameIndex++) {
+
+ //
+ // Check if this Name is completely unmapped (i.e. its domain
+ // has not yet been identified).
+ //
+
+ if (TranslatedSids->Sids[NameIndex].DomainIndex == LSA_UNKNOWN_INDEX) {
+
+ //
+ // Found a completely unmapped Name. If it belongs to the
+ // specified Domain, add the Domain to the Referenced Domain
+ // list if we have not already done so.
+ //
+
+ if (LsapRtlPrefixName(
+ (PUNICODE_STRING) DomainName,
+ (PUNICODE_STRING) &Names[NameIndex])
+ ) {
+
+ if (!DomainAlreadyAdded) {
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ DomainAlreadyAdded = TRUE;
+ }
+
+ //
+ // Reference the domain from the TranslatedNames entry
+ //
+
+ TranslatedSids->Sids[NameIndex].DomainIndex = DomainIndex;
+
+ //
+ // This name is now partially translated, so reduce the
+ // count of completely unmapped names.
+ //
+
+ (*CompletelyUnmappedCount)--;
+ }
+
+ //
+ // Decrement count of completely unmapped Names scanned.
+ //
+
+ RemainingCompletelyUnmappedCount--;
+ }
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbLookupDispatchWorkerThreads(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function dispatches sufficient worker threads to handle a
+ Lookup operation. The worker threads can handle work items
+ on any Lookup's Work List, so the number of existing active
+ threads is taken into account. Note that the total number of
+ active Lookup Worker Threads may exceed the guide maximum number of
+ threads, in situations where an active thread terminates during
+ the dispatch cycle. This strategy saves having to recheck the
+ active thread count each time a thread is dispatched.
+
+ NOTE: This routine expects the specified pointer to a Work List to be
+ valid. A Work List pointer always remains valid until its
+ Primary thread detects completion of the Work List via this
+ routine and then deletes it via LsapDbLookupDeleteWorkList().
+
+Arguments:
+
+ WorkList - Pointer to a Work List describing a Lookup Sid or Lookup Name
+ operation.
+
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS DispatchThreadStatus = STATUS_SUCCESS;
+ ULONG AdvisoryChildThreadCount;
+ ULONG DispatchThreadCount;
+ ULONG MaximumDispatchChildThreadCount;
+ ULONG ThreadNumber;
+ HANDLE Thread = NULL;
+ DWORD Ignore;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Acquire the Lookup Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupDispatchWorkerThreadsError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ //
+ // Calculate the number of Worker Threads to dispatch (if any). If
+ // the WorkList has an Advisory Child Thread Count of 0, we will
+ // not dispatch any threads, but instead will perform this Lookup
+ // within this thread. If the WorkList has an Advisory Child Thread
+ // Count > 0, we will dispatch additional threads. The number of
+ // additional child threads dispatched is given by the formula:
+ //
+ // ThreadsToDispatch =
+ // min (MaximumChildThreadCount - ActiveChildThreadCount,
+ // AdvisoryChildThreadCount)
+ //
+
+ AdvisoryChildThreadCount = WorkList->AdvisoryChildThreadCount;
+
+ if (AdvisoryChildThreadCount > 0) {
+
+ MaximumDispatchChildThreadCount =
+ LookupWorkQueue.MaximumChildThreadCount -
+ LookupWorkQueue.ActiveChildThreadCount;
+
+ if (AdvisoryChildThreadCount <= MaximumDispatchChildThreadCount) {
+
+ DispatchThreadCount = AdvisoryChildThreadCount;
+
+ } else {
+
+ DispatchThreadCount = MaximumDispatchChildThreadCount;
+ }
+
+ //
+ // Release the Lookup Work Queue Lock
+ //
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Signal the event that indicates that a new Work List is initiated.
+ //
+
+ Status = NtSetEvent( LsapDbLookupStartedEvent, NULL );
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("LsapDbLookupDispatchWorkList... NtSetEvent failed 0x%lx\n",Status));
+ goto LookupDispatchWorkerThreadsError;
+ }
+
+ //
+ // Dispatch the computed number of threads.
+ //
+
+ for (ThreadNumber = 0; ThreadNumber < DispatchThreadCount; ThreadNumber++) {
+
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) LsapDbLookupWorkerThreadStart,
+ NULL,
+ 0L,
+ &Ignore
+ );
+
+ if (Thread == NULL) {
+
+ Status = GetLastError();
+
+ KdPrint(("LsapDbLookupDispatchWorkerThreads: CreateThread failed 0x%lx\n"));
+
+ break;
+
+ } else {
+
+ CloseHandle( Thread );
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ DispatchThreadStatus = Status;
+ }
+ }
+
+ //
+ // Do some work in the main thread too.
+ //
+
+ LsapDbLookupWorkerThread( TRUE);
+
+LookupDispatchWorkerThreadsFinish:
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupDispatchWorkerThreadsError:
+
+ goto LookupDispatchWorkerThreadsFinish;
+}
+
+
+NTSTATUS
+LsapDbOpenPolicyTrustedDomain(
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE ControllerPolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens a handle to the Lsa Policy Database on a
+ DC for a given Trusted Domain. The Domain may be specified either
+ by Sid or by Name.
+
+Arguments:
+
+
+ TrustInformation - Specifies the Sid and/or Name of the Trusted Domain
+ whose Policy database is to be opened.
+
+ DesiredAccess - Specifies the desired access type to be granted.
+
+ ControllerPolicyHandle - Receives handle oto the LSA Policy Object
+ for the Lsa Policy database located on some DC for the specified
+ Trusted Domain.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_NO_MORE_ENTRIES - The DC list for the specified domain
+ is null.
+
+ Result codes from called routines.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS SecondaryStatus;
+ LSAPR_UNICODE_STRING DCName;
+ PLSAPR_UNICODE_STRING DomainName = NULL;
+ PLSAPR_TRUST_INFORMATION OutputTrustInformation = NULL;
+ LSAPR_HANDLE TrustedDomainHandle = NULL;
+ PLSAPR_TRUSTED_CONTROLLERS_INFO TrustedControllersInfo = NULL;
+ PLSAPR_SID DomainSid = NULL;
+ LSA_HANDLE OutputControllerPolicyHandle = NULL;
+ ULONG ControllerIndex, ControllerCount;
+
+ DCName.Length = DCName.MaximumLength = (USHORT) 0;
+ DCName.Buffer = NULL;
+
+ //
+ // If we're given the Domain Sid only, we need to get its name.
+ //
+
+ if ((TrustInformation->Name.Length == 0) ||
+ (TrustInformation->Name.Buffer == NULL)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (TrustInformation->Sid == NULL) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+
+ Status = LsapDbLookupSidTrustedDomainList(
+ NULL,
+ TrustInformation->Sid,
+ &OutputTrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+
+ DomainName = &OutputTrustInformation->Name;
+
+ } else {
+
+ DomainName = &TrustInformation->Name;
+ }
+
+ //
+ // Use netlogon services to locate a DC Name routine if possible. Even
+ // if the routine exists, it will return an error if netlogon.dll
+ // is not loaded.
+ //
+
+ Status = I_NetGetAnyDCName(
+ (PUNICODE_STRING) DomainName,
+ (PUNICODE_STRING) &DCName
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ if (DCName.Length != 0) {
+
+ Status = LsapRtlValidateControllerTrustedDomain(
+ &DCName,
+ TrustInformation,
+ DesiredAccess,
+ &OutputControllerPolicyHandle
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ goto OpenPolicyTrustedDomainFinish;
+ }
+ }
+ }
+
+ //
+ // An error occurred trying to obtain the name of a DC via net logon
+ // services. If the the error is simply that netlogon.dll is not loaded
+ // or running, fall back to the Trusted Controllers List in the LSA's
+ // TrustedDomain object for the Domain. Any other error means that
+ // there is also no point using the Trusted Controllers List either.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (!(Status == STATUS_NETLOGON_NOT_STARTED) ||
+ (Status == STATUS_DLL_NOT_FOUND)) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+ }
+
+ //
+ // We could not use netlogon.dll services to locate a DC only because
+ // netlogon.dll was not loaded and/or initilaized. Resort to the Trusted
+ // Controllers Info held in the TrustedDomain object. To access this
+ // object we first need to get the Domain Sid if we don't have it already.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ DomainSid = TrustInformation->Sid;
+
+ if (DomainSid == NULL) {
+
+ Status = LsapDbLookupNameTrustedDomainList(
+ NULL,
+ &TrustInformation->Name,
+ &OutputTrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+
+ DomainSid = OutputTrustInformation->Sid;
+ }
+
+ //
+ // Open the Trusted Domain object on the local machine.
+ //
+
+ Status = LsarOpenTrustedDomain(
+ LsapPolicyHandle,
+ DomainSid,
+ TRUSTED_QUERY_CONTROLLERS,
+ &TrustedDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+
+ goto OpenPolicyTrustedDomainFinish;
+ }
+
+ //
+ // Now obtain the list of Domain Controllers for the Primary Domain.
+ // Any error here is a problem with the local machine and should
+ // be reported. Note that no error is returned just because
+ // the trusted controllers info has never been set.
+ //
+
+ Status = LsarQueryInfoTrustedDomain(
+ TrustedDomainHandle,
+ TrustedControllersInformation,
+ (PLSAPR_TRUSTED_DOMAIN_INFO *) &TrustedControllersInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+
+ Status = STATUS_NO_MORE_ENTRIES;
+
+ ControllerCount = TrustedControllersInfo->Entries;
+
+ if (ControllerCount == (ULONG) 0) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+
+ //
+ // Pick a valid DC from the list and open its Policy Object.
+ // NOTE: This algorithm is non-optimal as the selection should
+ // randomly try all the controllers. This doesn't matter much since
+ // this code is executed only as a default when CliffV's algorithm
+ // is not implemented.
+ //
+
+ for (ControllerIndex = 0; ControllerIndex < ControllerCount; ControllerIndex++) {
+
+ DCName = TrustedControllersInfo->Names[ControllerIndex];
+
+ //
+ // Validate the next DC on the list and if valid, return a handle,
+ //
+
+ Status = LsapRtlValidateControllerTrustedDomain(
+ &DCName,
+ TrustInformation,
+ DesiredAccess,
+ &OutputControllerPolicyHandle
+ );
+
+ //
+ // Intentionally ignore the error status, except for the
+ // final controller tried.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ continue;
+ }
+
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenPolicyTrustedDomainError;
+ }
+
+OpenPolicyTrustedDomainFinish:
+
+ *ControllerPolicyHandle = OutputControllerPolicyHandle;
+
+ //
+ // If necessary, close the Trusted Domain object handle.
+ //
+
+ if (TrustedDomainHandle != NULL) {
+
+ SecondaryStatus = LsarClose( &TrustedDomainHandle );
+
+ TrustedDomainHandle = NULL;
+ }
+
+ //
+ // If necessary, free memory allocated for the DCName buffer.
+ //
+
+ if (DCName.Buffer != NULL) {
+
+ MIDL_user_free( DCName.Buffer );
+ }
+
+ //
+ // If allocated, free the TrustedControllersInfo top-level structure
+ //
+
+ if (TrustedControllersInfo != NULL) {
+
+ MIDL_user_free(TrustedControllersInfo);
+ }
+
+ return(Status);
+
+OpenPolicyTrustedDomainError:
+
+ goto OpenPolicyTrustedDomainFinish;
+}
+
+
+NTSTATUS
+LsapRtlValidateControllerTrustedDomain(
+ IN PLSAPR_UNICODE_STRING DomainControllerName,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE ControllerPolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that a specified computer is a DC for
+ a specified Domain and opens the LSA Policy Object with the
+ desired accesses.
+
+Arguments:
+
+ DomainControllerName - Pointer to Unicode String computer name.
+
+ TrustInformation - Domain Trust Information. Only the Sid is
+ used.
+
+ DesiredAccess - Specifies the accesses desired to the target machine's
+ Lsa Policy Database.
+
+ PolicyHandle - Receives handle to the Lsa Policy object on the target
+ machine.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_OBJECT_NAME_NOT_FOUND - The specified computer is not a
+ Domain Controller for the specified Domain.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE OutputControllerPolicyHandle = NULL;
+ PLSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo = NULL;
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
+
+ //
+ // Open a handle to the Policy Object on the target DC.
+ //
+
+ SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
+ SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQualityOfService.EffectiveOnly = FALSE;
+
+ //
+ // Set up the object attributes prior to opening the LSA.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL
+ );
+
+ //
+ // The InitializeObjectAttributes macro presently stores NULL for
+ // the SecurityQualityOfService field, so we must manually copy that
+ // structure for now.
+ //
+
+ ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
+
+ Status = LsaOpenPolicy(
+ (PUNICODE_STRING) DomainControllerName,
+ &ObjectAttributes,
+ POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
+ &OutputControllerPolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ValidateControllerTrustedDomainError;
+ }
+
+ //
+ // We successfully opened the Policy object on the Controller's
+ // Lsa. Check that the target system is a DC for the Domain. We
+ // verify that its Primary Domain and Account Domain information
+ // both specify the given domain. This is a hack because there is
+ // no equivalent of RtlGetNtProductType for remote systems. So,
+ // first compare the Primary Domain Info...
+ //
+
+ Status = LsaQueryInformationPolicy(
+ OutputControllerPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ValidateControllerTrustedDomainError;
+ }
+
+ //
+ // Now compare the Domain Name and Sid stored for the Controller's
+ // Primary Domain with the Domain Name and Sid provided.
+ //
+
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (!RtlEqualDomainName(
+ (PUNICODE_STRING) &TrustInformation->Name,
+ (PUNICODE_STRING) &PolicyPrimaryDomainInfo->Name
+ )) {
+
+ goto ValidateControllerTrustedDomainError;
+ }
+
+ if (!RtlEqualSid(
+ (PSID) TrustInformation->Sid,
+ (PSID) PolicyPrimaryDomainInfo->Sid
+ )) {
+
+ goto ValidateControllerTrustedDomainError;
+ }
+
+ //
+ // Now compare the Account Domain info.
+ //
+
+ Status = LsaQueryInformationPolicy(
+ OutputControllerPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ValidateControllerTrustedDomainError;
+ }
+
+ //
+ // Now compare the Domain Name and Sid stored for the Controller's
+ // Account Domain with the Domain Name and Sid provided.
+ //
+
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (!RtlEqualDomainName(
+ (PUNICODE_STRING) &TrustInformation->Name,
+ (PUNICODE_STRING) &PolicyAccountDomainInfo->DomainName
+ )) {
+
+ goto ValidateControllerTrustedDomainError;
+ }
+
+ if (!RtlEqualSid(
+ (PSID) TrustInformation->Sid,
+ (PSID) PolicyAccountDomainInfo->DomainSid
+ )) {
+
+ goto ValidateControllerTrustedDomainError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ValidateControllerTrustedDomainFinish:
+
+ //
+ // Free up the Policy Primary Domain Info.
+ //
+
+ if (PolicyPrimaryDomainInfo != NULL) {
+
+ LsaFreeMemory( (PVOID) PolicyPrimaryDomainInfo );
+
+ PolicyPrimaryDomainInfo = NULL;
+ }
+
+ //
+ // Free up the Policy Account Domain Info.
+ //
+
+ if (PolicyAccountDomainInfo != NULL) {
+
+ LsaFreeMemory( (PVOID) PolicyAccountDomainInfo );
+
+ PolicyAccountDomainInfo = NULL;
+ }
+
+ //
+ // Return Controller Policy handle or NULL.
+ //
+
+ *ControllerPolicyHandle = OutputControllerPolicyHandle;
+
+ return(Status);
+
+ValidateControllerTrustedDomainError:
+
+ //
+ // Close the last Controller's Policy Handle.
+ //
+
+ if (OutputControllerPolicyHandle != NULL) {
+
+ SecondaryStatus = LsaClose( OutputControllerPolicyHandle );
+ OutputControllerPolicyHandle = NULL;
+ }
+
+ goto ValidateControllerTrustedDomainFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupAcquireWorkQueueLock(
+ )
+
+/*++
+
+Routine Description:
+
+ This function acquires the LSA Database Lookup Sids/Names Work Queue Lock.
+ This lock serializes additions or deletions of work lists to/from the
+ queue, and sign-in/sign-out of work items by worker threads.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ Status = RtlEnterCriticalSection(&LookupWorkQueue.Lock);
+
+ return(Status);
+}
+
+
+VOID
+LsapDbLookupReleaseWorkQueueLock(
+ )
+
+/*++
+
+Routine Description:
+
+ This function releases the LSA Database Lookup Sids/Names Work Queue Lock.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None. Any error occurring within this routine is an internal error.
+
+--*/
+
+{
+ RtlLeaveCriticalSection(&LookupWorkQueue.Lock);
+}
+
+
+NTSTATUS
+LsapDbLookupNamesBuildWorkList(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs a Work List for an LsarLookupNames call. The
+ Work List contains the parameters of the call, and an array of Work Items.
+ Each Work Item specifies either all of the Names to be looked up
+ in a given domain.
+
+ Qualified names (i.e. those of the form DomainName\UserName) are
+ sorted into different Work Items, one for each DomainName specifed.
+ Unqualified names (i.e. those of the form UserName) are added to
+ every Work Item.
+
+Arguments:
+
+ Count - Specifies the number of names to be translated.
+
+ Names - Pointer to an array of Count Unicode String structures
+ specifying the names to be looked up and mapped to Sids.
+ The strings may be names of User, Group or Alias accounts or
+ domains.
+
+ PrefixNames - Pointer to an array of Count Unicode String structures
+ containing the Prefix portions of the Names. Names having no
+ Prefix are called Isolated Names. For these, the Unicode String
+ structure is set to contain a zero Length.
+
+ SuffixNames - Pointer to an array of Count Unicode String structures
+ containing the Suffix portions of the Names.
+
+ ReferencedDomains - receives a pointer to a structure describing the
+ domains used for the translation. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for
+ each translated name, this structure will only contain one
+ component for each domain utilized in the translation.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ TranslatedSids - Pointer to a structure which will (or already) references an array of
+ records describing each translated Sid. The nth entry in this array
+ provides a translation for the nth element in the Names parameter.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupWksta - First Level Lookup performed on a workstation
+ normally configured for Windows-Nt. The lookup searches the
+ Well-Known Sids/Names, and the Built-in Domain and Account Domain
+ in the local SAM Database. If not all Sids or Names are
+ identified, performs a "handoff" of a Second level Lookup to the
+ LSA running on a Controller for the workstation's Primary Domain
+ (if any).
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids or Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ MappedCount - Pointer to location that contains a count of the Names
+ mapped so far. On exit, this will be updated.
+
+ MappedCount - Pointer to location containing the number of Names
+ in the Names array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Names. A Name is completely unmapped
+ if it is unknown and non-composite, or composite but with an
+ unrecognized Domain component. This count is updated on exit, the
+ number of completely unmapped Namess whose Domain Prefices are
+ identified by this routine being subtracted from the input value.
+
+ WorkList - Receives pointer to completed Work List if successfully built.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_NONE_MAPPED - All of the Names specified were composite,
+ but none of their Domains appear in the Trusted Domain List.
+ No Work List has been generated. Note that this is not a
+ fatal error.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS, IgnoreStatus = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
+ ULONG NameIndex;
+ PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
+ PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
+ LONG DomainIndex;
+ PLSAP_DB_LOOKUP_WORK_ITEM IsolatedNamesWorkItem = NULL;
+ BOOLEAN AcquiredTrustedDomainListReadLock = FALSE;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION TrustedDomainListSection = NULL;
+ ULONG TrustedDomainListSectionIndex;
+ PLSAPR_UNICODE_STRING DomainName = NULL;
+ PLSAPR_UNICODE_STRING TerminalName = NULL;
+
+ //
+ // Create an empty Work List.
+ //
+
+ Status = LsapDbLookupCreateWorkList(&OutputWorkList);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+
+ //
+ // Initialize the Work List Header.
+ //
+
+ OutputWorkList->Status = STATUS_SUCCESS;
+ OutputWorkList->State = InactiveWorkList;
+ OutputWorkList->LookupType = LookupNames;
+ OutputWorkList->Count = Count;
+ OutputWorkList->LookupLevel = LookupLevel;
+ OutputWorkList->ReferencedDomains = ReferencedDomains;
+ OutputWorkList->MappedCount = MappedCount;
+ OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
+ OutputWorkList->LookupNamesParams.Names = Names;
+ OutputWorkList->LookupNamesParams.TranslatedSids = TranslatedSids;
+
+ //
+ // Construct the array of Work Items. Each Work Item will
+ // contain all the Names for a given domain, so we will scan
+ // all of the Names, sorting them into Work Items as we go.
+ // For each Name, follow the steps detailed below.
+ //
+
+ for (NameIndex = 0; NameIndex < Count; NameIndex++) {
+
+ //
+ // If this Name's Domain is already marked as known, skip.
+ //
+
+ if (TranslatedSids->Sids[NameIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
+
+ continue;
+ }
+
+ //
+ // Name is completely unknown. See if there is already a Work Item
+ // for its Domain.
+ //
+
+ AnchorWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem;
+ NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) AnchorWorkItem->Links.Flink;;
+ WorkItemToUpdate = NULL;
+ NewWorkItem = NULL;
+
+ //
+ // If this is a qualified name (e.g. "NtDev\ScottBi") proceed
+ // to search for its Domain.
+ //
+
+ DomainName = &PrefixNames[ NameIndex ];
+ TerminalName = &SuffixNames[ NameIndex ];
+
+ if (DomainName->Length != 0) {
+
+ while (NextWorkItem != AnchorWorkItem) {
+
+ if (RtlEqualDomainName(
+ (PUNICODE_STRING) &NextWorkItem->TrustInformation.Name,
+ (PUNICODE_STRING) DomainName
+ )) {
+
+ //
+ // A Work Item already exists for the Name's Trusted Domain.
+ // Select that Work Item for update.
+ //
+
+ WorkItemToUpdate = NextWorkItem;
+
+ break;
+ }
+
+ //
+ // Name's domain not found among existing Work Items. Skip to
+ // next Work Item.
+ //
+
+ NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
+ }
+
+ if (WorkItemToUpdate == NULL) {
+
+ //
+ // No Work Item exists for the Name's Domain. See if the
+ // Name belongs to one of the Trusted Domains. If not, skip
+ // to the next Name.
+ //
+
+ if (!AcquiredTrustedDomainListReadLock) {
+
+ Status = LsapDbAcquireReadLockTrustedDomainList( NULL );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AcquiredTrustedDomainListReadLock = TRUE;
+ }
+
+ Status = LsapDbLookupNameTrustedDomainList(
+ NULL,
+ DomainName,
+ &TrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status == STATUS_NO_SUCH_DOMAIN) {
+
+ Status = STATUS_SUCCESS;
+ continue;
+ }
+
+ break;
+ }
+
+ //
+ // Name belongs to a Trusted Domain for which there is
+ // no Work Item. Add the Domain to the Referenced Domain List
+ // and obtain a Domain Index.
+ //
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Create a new Work Item for this domain.
+ //
+
+ Status = LsapDbLookupCreateWorkItem(
+ TrustInformation,
+ DomainIndex,
+ (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
+ &NewWorkItem
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Add the Work Item to the List.
+ //
+
+ Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ WorkItemToUpdate = NewWorkItem;
+ }
+
+ //
+ // Add the Name Index to the Work Item.
+ //
+
+ Status = LsapDbLookupAddIndicesToWorkItem(
+ WorkItemToUpdate,
+ (ULONG) 1,
+ &NameIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Store the Domain Index in the Translated Sids array entry for
+ // the Name.
+ //
+
+ OutputWorkList->LookupNamesParams.TranslatedSids->Sids[NameIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+
+ } else {
+
+ //
+ // This is an Isolated Name. We know that it is not the name
+ // of any Domain on the lookup path, because all of these
+ // have been translated earlier. We will add the name to a
+ // temporary work item and later transfer all the Isolated Names
+ // to every Work Item on the Work List. If we don't already
+ // have a temporary Work Item for the Isolated Names, create one.
+ //
+
+ if (IsolatedNamesWorkItem == NULL) {
+
+ //
+ // Create a new Work Item for the Isolated Names.
+ // This is temporary.
+ //
+
+ Status = LsapDbLookupCreateWorkItem(
+ NULL,
+ DomainIndex,
+ (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
+ &IsolatedNamesWorkItem
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ //
+ // Mark this Work Item as having Isolated Names only.
+ //
+
+ IsolatedNamesWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;
+
+ //
+ // Add the Name index to the Isolated Names Work Item
+ //
+
+ Status = LsapDbLookupAddIndicesToWorkItem(
+ IsolatedNamesWorkItem,
+ (ULONG) 1,
+ &NameIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+
+ //
+ // If we have any unmapped Isolated Names, we know at this stage that they are
+ // not the Names of Trusted Domains. Therefore, we need to arrange
+ // for them to be looked up in every Trusted Domain. We need to
+ // traverse the list of Trusted Domains and for each Domain, either
+ // create a new Work Item to lookup all of the Names in that Domain,
+ // or add the Names to the existing Work Item generated for that
+ // Domain (because there are some Qualified Names which reference
+ // the Domain. We do this Work Item Generation in 2 stages. First,
+ // we will scan the Work List, adding the Isolated Names to every Work
+ // Item found therein. Second, we will create a Work Item for each
+ // of the Trusted Domains that we don't already have a Work Item.
+ //
+
+ //
+ // Stage (1) - Scan the Work List, adding the Isolated Names to
+ // every Work Item found therein
+ //
+
+ if (IsolatedNamesWorkItem != NULL) {
+
+ //
+ // Stage (1) - Scan the Work List, adding the Isolated Names to
+ // every Work Item found therein
+ //
+
+ NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
+ OutputWorkList->AnchorWorkItem->Links.Flink;
+
+ while (NextWorkItem != OutputWorkList->AnchorWorkItem) {
+
+ //
+ // Add the Isolated Name indices to this Work Item
+ //
+
+ Status = LsapDbLookupAddIndicesToWorkItem(
+ NextWorkItem,
+ IsolatedNamesWorkItem->UsedCount,
+ IsolatedNamesWorkItem->Indices
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
+ NextWorkItem->Links.Flink;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+
+ //
+ // Stage (2) - Now create Work Items to look up all the Isolated Names
+ // in every Trusted Domain that does not presently have a Work Item.
+ // The Domains for all existing Work Items are already present
+ // on the Referenced Domains List because they all specify at least one
+ // Qualified Name, so we can lookup that list to determine whether a
+ // Work Item exists for a Domain.
+ //
+
+ TrustedDomainListSection = NULL;
+ TrustedDomainListSectionIndex = (ULONG) 0;
+
+ for (;;) {
+
+ NewWorkItem = NULL;
+
+ Status = LsapDbTraverseTrustedDomainList(
+ NULL,
+ &TrustedDomainListSection,
+ &TrustedDomainListSectionIndex,
+ &TrustInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+
+ Status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ //
+ // Found Next Trusted Domain. Check if this domain is already
+ // present on the Referenced Domain List. If so, skip to the
+ // next domain, because we have a Work Item for this domain.
+ //
+ // If the Domain is not present on the Referenced Domain List,
+ // we need to create a Work Item for this Domain and add all
+ // of the unmapped Isolated Names indices to it.
+ //
+
+ if (LsapDbLookupListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation->Sid,
+ &DomainIndex
+ )) {
+
+ continue;
+ }
+
+ //
+ // We don't have a Work Item for this Trusted Domain. Create
+ // one, and add all of the remaining Isolated Names to it.
+ // Mark the Domain Index as unknown. This is picked up
+ // later when the Work Item is processed. If any Names
+ // were translated, the Domain to which the Work Item
+ // relates will be added to the Referenced Domain List.
+ //
+
+ Status = LsapDbLookupCreateWorkItem(
+ TrustInformation,
+ LSA_UNKNOWN_INDEX,
+ (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
+ &NewWorkItem
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Mark this Work Item as having Isolated Names only.
+ //
+
+ NewWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;
+
+ //
+ // Add the Isolated Name indices to this Work Item
+ //
+
+ Status = LsapDbLookupAddIndicesToWorkItem(
+ NewWorkItem,
+ IsolatedNamesWorkItem->UsedCount,
+ IsolatedNamesWorkItem->Indices
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Add the Work Item to the List.
+ //
+
+ Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+ }
+
+ //
+ // If the Work List has no Work Items, this means that all of the
+ // Names were composite, but none of the Domain Names specified
+ // could be found on the Trusted Domain List. In this case,
+ // we discard the Work List.
+ //
+
+ Status = STATUS_NONE_MAPPED;
+
+ if (OutputWorkList->WorkItemCount == 0) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+
+ //
+ // Compute the Advisory Thread Count for this lookup.
+ //
+
+ Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+
+ Status = LsapDbLookupInsertWorkList(OutputWorkList);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesBuildWorkListError;
+ }
+
+ //
+ // Update the Mapped Counts
+ //
+
+ LsapDbUpdateMappedCountsWorkList( OutputWorkList );
+
+LookupNamesBuildWorkListFinish:
+
+ //
+ // If necessary, release the Trusted Domain List Read Lock.
+ //
+
+ if (AcquiredTrustedDomainListReadLock) {
+
+ LsapDbReleaseReadLockTrustedDomainList( NULL );
+ AcquiredTrustedDomainListReadLock = FALSE;
+ }
+
+ //
+ // If we have an Isolated Names Work Item, free it.
+ //
+
+ if (IsolatedNamesWorkItem != NULL) {
+
+ MIDL_user_free( IsolatedNamesWorkItem->Indices);
+ IsolatedNamesWorkItem->Indices = NULL;
+ MIDL_user_free( IsolatedNamesWorkItem );
+ IsolatedNamesWorkItem = NULL;
+ }
+
+ *WorkList = OutputWorkList;
+ return(Status);
+
+LookupNamesBuildWorkListError:
+
+ //
+ // Discard the Work List.
+ //
+
+ if (OutputWorkList != NULL) {
+
+ IgnoreStatus = LsapDbLookupDeleteWorkList(OutputWorkList);
+ OutputWorkList = NULL;
+ }
+
+ goto LookupNamesBuildWorkListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupSidsBuildWorkList(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs a Work List for an LsarLookupSids call. The
+ Work List contains the parameters of the call, and an array of Work Items.
+ Each Work Item specifies all of the Sids belonging to a given Domain
+ and is the minimal unit of work that a worker thread will undertake.
+
+ Count - Number of Sids in the Sids array, Note that some of these
+ may already have been mapped elsewhere, as specified by the
+ MappedCount parameter.
+
+ Sids - Pointer to array of pointers to Sids to be translated.
+ Zero or all of the Sids may already have been translated
+ elsewhere. If any of the Sids have been translated, the
+
+ Names parameter will point to a location containing a non-NULL
+ array of Name translation structures corresponding to the
+ Sids. If the nth Sid has been translated, the nth name
+ translation structure will contain either a non-NULL name
+ or a non-negative offset into the Referenced Domain List. If
+ the nth Sid has not yet been translated, the nth name
+ translation structure will contain a zero-length name string
+ and a negative value for the Referenced Domain List index.
+
+ TrustInformation - Pointer to Trust Information specifying a Domain Sid
+ and Name.
+
+ ReferencedDomains - Pointer to a Referenced Domain List structure.
+ The structure references an array of zero or more Trust Information
+ entries, one per referenced domain. This array will be appended to
+ or reallocated if necessary.
+
+ TranslatedNames - Pointer to structure that optionally references a list
+ of name translations for some of the Sids in the Sids array.
+
+ LookupLevel - Specifies the Level of Lookup to be performed on this
+ machine. Values of this field are are follows:
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids or Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ NOTE: LsapLookupWksta is not valid for this parameter.
+
+ MappedCount - Pointer to location containing the number of Sids
+ in the Sids array that have already been mapped. This number
+ will be updated to reflect additional mapping done by this
+ routine.
+
+ CompletelyUnmappedCount - Pointer to location containing the
+ count of completely unmapped Sids. A Sid is completely unmapped
+ if it is unknown and also its Domain Prefix Sid is not recognized.
+ This count is updated on exit, the number of completely unmapped
+ Sids whose Domain Prefices are identified by this routine being
+ subtracted from the input value.
+
+ WorkList - Receives pointer to completed Work List if successfully built.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_NONE_MAPPED - None of the Sids specified belong to any of
+ the Trusted Domains. No Work List has been generated. Note
+ that this is not a fatal error.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS IgnoreStatus = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
+ ULONG SidIndex;
+ PSID DomainSid = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
+ PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
+ LONG DomainIndex;
+ PSID Sid = NULL;
+ BOOLEAN AcquiredReadLockTrustedDomainList = FALSE;
+
+ //
+ // Create an empty Work List
+ //
+
+ Status = LsapDbLookupCreateWorkList(&OutputWorkList);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsBuildWorkListError;
+ }
+
+ //
+ // Initialize the rest of the Work List Header fields. Some fields
+ // were initialized upon creation to fixed values. The ones set here
+ // depend on parameter values passed into this routine.
+ //
+
+ OutputWorkList->LookupType = LookupSids;
+ OutputWorkList->Count = Count;
+ OutputWorkList->LookupLevel = LookupLevel;
+ OutputWorkList->ReferencedDomains = ReferencedDomains;
+ OutputWorkList->MappedCount = MappedCount;
+ OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
+ OutputWorkList->LookupSidsParams.Sids = Sids;
+ OutputWorkList->LookupSidsParams.TranslatedNames = TranslatedNames;
+
+ //
+ // Construct the array of Work Items. Each Work Item will
+ // contain all the Sids for a given domain, so we will scan
+ // all of the Sids, sorting them into Work Items as we go.
+ // For each Sid, follow the steps detailed below.
+ //
+
+ for (SidIndex = 0; SidIndex < Count; SidIndex++) {
+
+ //
+ // If this Sid's Domain is already marked as known, skip.
+ //
+
+ if (TranslatedNames->Names[SidIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
+
+ continue;
+ }
+
+ //
+ // Sid is completely unknown. Extract its Domain Sid and see if
+ // there is already a Work Item for its Domain.
+ //
+
+ Sid = Sids[SidIndex];
+
+ Status = LsapRtlExtractDomainSid( Sid, &DomainSid );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem->Links.Flink;
+ AnchorWorkItem = OutputWorkList->AnchorWorkItem;
+ WorkItemToUpdate = NULL;
+ NewWorkItem = NULL;
+
+ while (NextWorkItem != AnchorWorkItem) {
+
+ if (RtlEqualSid((PSID) NextWorkItem->TrustInformation.Sid,DomainSid)) {
+
+ //
+ // A Work Item already exists for the Sid's Trusted Domain.
+ // Select that Work Item for update.
+ //
+
+ MIDL_user_free(DomainSid);
+ WorkItemToUpdate = NextWorkItem;
+ break;
+ }
+
+ //
+ // Sid's domain not found among existing Work Items. Skip to
+ // next Work Item.
+ //
+
+ NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
+ }
+
+ if (WorkItemToUpdate == NULL) {
+
+ //
+ // No Work Item exists for the Sid's Domain. See if the
+ // Sid belongs to one of the Trusted Domains. If not, skip
+ // to the next Sid.
+ //
+
+ if (!AcquiredReadLockTrustedDomainList) {
+
+ Status = LsapDbAcquireReadLockTrustedDomainList( NULL );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AcquiredReadLockTrustedDomainList = TRUE;
+ }
+
+ Status = LsapDbLookupSidTrustedDomainList(
+ NULL,
+ DomainSid,
+ &TrustInformation
+ );
+
+ MIDL_user_free(DomainSid);
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if (Status == STATUS_NO_SUCH_DOMAIN) {
+
+ //
+ // Try looking up the sid itself as a domain
+ //
+
+ Status = LsapDbLookupSidTrustedDomainList(
+ NULL,
+ Sid,
+ &TrustInformation
+ );
+
+
+ if (Status == STATUS_NO_SUCH_DOMAIN) {
+
+ Status = STATUS_SUCCESS;
+ continue;
+ }
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+ break;
+ }
+ }
+
+ //
+ // Sid belongs to a Trusted Domain for which there is
+ // no Work Item. Add the Domain to the Referenced Domain List
+ // and obtain a Domain Index.
+ //
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ &DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Create a new Work Item for this domain.
+ //
+
+ Status = LsapDbLookupCreateWorkItem(
+ TrustInformation,
+ DomainIndex,
+ (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
+ &NewWorkItem
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Add the Work Item to the List.
+ //
+
+ Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ WorkItemToUpdate = NewWorkItem;
+ }
+
+ //
+ // Add the Sid Index to the Work Item.
+ //
+
+ Status = LsapDbLookupAddIndicesToWorkItem(
+ WorkItemToUpdate,
+ (ULONG) 1,
+ &SidIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Store the Domain Index in the Translated Names array entry for
+ // the Sid.
+ //
+
+ OutputWorkList->LookupSidsParams.TranslatedNames->Names[SidIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsBuildWorkListError;
+ }
+
+ //
+ // If the Work List has no Work Items, this means that none of the
+ // Sids belong to any of the Trusted Domains. In this case,
+ // we discard the Work List.
+ //
+
+ Status = STATUS_NONE_MAPPED;
+
+ if (OutputWorkList->WorkItemCount == 0) {
+
+ goto LookupSidsBuildWorkListError;
+ }
+
+ //
+ // Compute the Advisory Thread Count for this lookup.
+ //
+
+ Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsBuildWorkListError;
+ }
+
+ //
+ // Insert the Work List at the end of the Work Queue.
+ //
+
+ Status = LsapDbLookupInsertWorkList(OutputWorkList);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsBuildWorkListError;
+ }
+
+ //
+ // Update the Mapped Counts
+ //
+
+ LsapDbUpdateMappedCountsWorkList( OutputWorkList );
+
+ *WorkList = OutputWorkList;
+
+LookupSidsBuildWorkListFinish:
+
+ //
+ // If necessary, release the Trusted Domain List Read Lock.
+ //
+
+ if (AcquiredReadLockTrustedDomainList) {
+
+ LsapDbReleaseReadLockTrustedDomainList( NULL );
+ AcquiredReadLockTrustedDomainList = FALSE;
+ }
+
+ return(Status);
+
+LookupSidsBuildWorkListError:
+
+ if ( OutputWorkList != NULL ) {
+
+ IgnoreStatus = LsapDbLookupDeleteWorkList( OutputWorkList );
+ OutputWorkList = NULL;
+ }
+
+ *WorkList = NULL;
+ goto LookupSidsBuildWorkListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupCreateWorkList(
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a Lookup Operation Work List and
+ initializes fixed default fields.
+
+Arguments:
+
+ WorkList - Receives Pointer to an empty Work List structure.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Allocate memory for the Work List header.
+ //
+
+ *WorkList = LsapAllocateLsaHeap( sizeof(LSAP_DB_LOOKUP_WORK_LIST) );
+
+ //
+ // Initialize the fixed fields in the Work List.
+ //
+
+ Status = LsapDbLookupInitializeWorkList(*WorkList);
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbLookupInsertWorkList(
+ IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function inserts a Lookup Operation Work List in the Work Queue.
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a Lookup Sids
+ or Lookup Names operation.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Acquire the Lookup Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupInsertWorkListError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ //
+ // Mark the Work List as Active.
+ //
+
+ WorkList->State = ActiveWorkList;
+
+ //
+ // Link the Work List onto the end of the Work Queue.
+ //
+
+ WorkList->WorkLists.Flink =
+ (PLIST_ENTRY) LookupWorkQueue.AnchorWorkList;
+ WorkList->WorkLists.Blink =
+ (PLIST_ENTRY) LookupWorkQueue.AnchorWorkList->WorkLists.Blink;
+
+ WorkList->WorkLists.Flink->Blink = (PLIST_ENTRY) WorkList;
+ WorkList->WorkLists.Blink->Flink = (PLIST_ENTRY) WorkList;
+
+ //
+ // Update the Currently Assignable Work List and Work Item pointers
+ // if there is none.
+ //
+
+ if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
+
+ LookupWorkQueue.CurrentAssignableWorkList = WorkList;
+ LookupWorkQueue.CurrentAssignableWorkItem =
+ (PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Flink;
+ }
+
+
+ //
+ // Diagnostic message indicating work list has been inserted
+ //
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("LSA DB: Inserting WorkList: 0x%lx ( Item Count: %ld)\n", WorkList, WorkList->WorkItemCount) );
+
+
+LookupInsertWorkListFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupInsertWorkListError:
+
+ goto LookupInsertWorkListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupDeleteWorkList(
+ IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function Deletes a Lookup Operation Work List from the Work Queue
+ and frees the Work List structure.
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a Lookup Sids
+ or Lookup Names operation.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_ITEM ThisWorkItem = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Acquire the Lookup Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupDeleteWorkListError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ //
+ // An internal error exists if we are trying to delete an active Work List.
+ // Only inactive or completed Work Lists can be removed.
+ //
+
+ ASSERT(WorkList->State != ActiveWorkList);
+
+ //
+ // If the Work List is on the Work Queue, remove it.
+ //
+
+ if ((WorkList->WorkLists.Blink != NULL) &&
+ (WorkList->WorkLists.Flink != NULL)) {
+
+ WorkList->WorkLists.Blink->Flink = WorkList->WorkLists.Flink;
+ WorkList->WorkLists.Flink->Blink = WorkList->WorkLists.Blink;
+ }
+
+ //
+ // Release the Lookup Work Queue Lock.
+ //
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Free up memory allocated for the Work Items on the List.
+ //
+
+ ThisWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Blink;
+
+ while (ThisWorkItem != WorkList->AnchorWorkItem) {
+
+ NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) ThisWorkItem->Links.Blink;
+
+ if (ThisWorkItem->Indices != NULL) {
+
+ MIDL_user_free( ThisWorkItem->Indices );
+ }
+
+ MIDL_user_free( ThisWorkItem->TrustInformation.Sid );
+
+ MIDL_user_free( ThisWorkItem->TrustInformation.Name.Buffer );
+
+ MIDL_user_free( ThisWorkItem );
+
+ ThisWorkItem = NextWorkItem;
+ }
+
+ //
+ // Free up memory allocated for the Work List structure itself.
+ //
+
+ MIDL_user_free( WorkList );
+
+LookupDeleteWorkListFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupDeleteWorkListError:
+
+ goto LookupDeleteWorkListFinish;
+}
+
+
+VOID
+LsapDbUpdateMappedCountsWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Decsription:
+
+ This function updates the counts of completely Mapped and completely
+ Unmapped Sids or Names in a Work List. A Sid or Name is completely
+ mapped if its Use has been identified, partially mapped if its
+ Domain is known but its terminal name of relative id is not yet
+ known, completely unmapped if its domain is not yet known.
+
+Arguments:
+
+ WorkList - Pointer to Work List to be updated.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+ ULONG OutputMappedCount = (ULONG) 0;
+ ULONG OutputCompletelyUnmappedCount = WorkList->Count;
+ ULONG Index;
+
+ //
+ // Acquire the Lookup Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UpdateMappedCountsWorkListError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ if (WorkList->LookupType == LookupSids) {
+
+ for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {
+
+ if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].Use
+ != SidTypeUnknown) {
+
+ OutputMappedCount++;
+ OutputCompletelyUnmappedCount--;
+
+ } else if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].DomainIndex
+ != LSA_UNKNOWN_INDEX) {
+
+ OutputCompletelyUnmappedCount--;
+ }
+ }
+
+ } else {
+
+ for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {
+
+ if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].Use
+ != SidTypeUnknown) {
+
+ OutputMappedCount++;
+ OutputCompletelyUnmappedCount--;
+
+ } else if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].DomainIndex
+ != LSA_UNKNOWN_INDEX) {
+
+ OutputCompletelyUnmappedCount--;
+ }
+ }
+ }
+
+ *WorkList->MappedCount = OutputMappedCount;
+ *WorkList->CompletelyUnmappedCount = OutputCompletelyUnmappedCount;
+
+UpdateMappedCountsWorkListFinish:
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return;
+
+UpdateMappedCountsWorkListError:
+
+ goto UpdateMappedCountsWorkListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupSignalCompletionWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function signals the completion or termination of work on
+ a Work List.
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a Lookup Sids
+ or Lookup Names operation.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Verify that all work on the Work List is either complete or
+ // the Work List has been terminated.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSignalCompletionWorkListError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ if (NT_SUCCESS(WorkList->Status)) {
+
+ Status = STATUS_INTERNAL_DB_ERROR;
+
+ if (WorkList->CompletedWorkItemCount != WorkList->WorkItemCount) {
+
+ goto LookupSignalCompletionWorkListError;
+ }
+ }
+
+ //
+ // Signal the event that indicates that a Work List has been processed.
+ //
+
+ Status = NtSetEvent( LsapDbLookupCompleteEvent, NULL );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("LSA DB: LsapDbLookupSignalCompletion.. NtSetEvent failed 0x%lx\n",Status));
+ goto LookupSignalCompletionWorkListError;
+ }
+
+LookupSignalCompletionWorkListFinish:
+
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("LSA DB: Lookup completion event signalled. (Status: 0x%lx)\n"
+ " WorkList: 0x%lx\n", Status, WorkList) );
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupSignalCompletionWorkListError:
+
+ goto LookupSignalCompletionWorkListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupAwaitCompletionWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function awaits the completion or termination of work on a
+ specified Work List.
+
+ NOTE: This routine expects the specified pointer to a Work List to be
+ valid. A Work List pointer always remains valid until its
+ Primary thread detects completion of the Work List via this
+ routine and then deletes it via LsapDbLookupDeleteWorkList().
+ For this reason, the Lookup Work Queue lock does not have to
+ be held while this routine executes.
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a Lookup Sids
+ or Lookup Names operation.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_LOOKUP_WORK_LIST_STATE WorkListState;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Loop, waiting for completion events to occur. When one does,
+ // check the status of the specified Work List.
+ //
+
+ for (;;) {
+
+ //
+ // Check for completed Work List. Since someone else may be
+ // setting the state, we need to read it while holding the lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ WorkListState = WorkList->State;
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+
+ if (WorkListState == CompletedWorkList) {
+
+ break;
+ }
+
+ //
+ // Wait for Work List completed event to be signalled.
+ //
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("Lsa Db: Waiting on worklist completion event\n") );
+ Status = NtWaitForSingleObject( LsapDbLookupCompleteEvent, TRUE, NULL);
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LsapDb: Wait on worklist completion event Done\n Status: 0x%lx\n", Status) );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupAwaitCompletionWorkListError;
+ }
+
+LookupAwaitCompletionWorkListFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupAwaitCompletionWorkListError:
+
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("Lsa Db: LookupAwaitWorklist error. (Status: 0x%lx)\n", Status) );
+
+ goto LookupAwaitCompletionWorkListFinish;
+}
+
+
+NTSTATUS
+LsapDbAddWorkItemToWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
+ )
+
+/*++
+
+Routine Decsription:
+
+ This function adds a Work Item to a Work List. The specified
+ Work Item must exist in non-volatile memory (e.g a heap block).
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a Lookup Sids
+ or Lookup Names operation.
+
+ WorkItem - Pointer to a Work Item structure describing a list of
+ Sids or Names and a domain in which they are to be looked up.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Acquire the Lookup Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupAddWorkItemToWorkListError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ //
+ // Mark the Work Item as assignable.
+ //
+
+ WorkItem->State = AssignableWorkItem;
+
+ //
+ // Link the Work Item onto the end of the Work List and increment the
+ // Work Item Count.
+ //
+
+ WorkItem->Links.Flink = (PLIST_ENTRY) WorkList->AnchorWorkItem;
+ WorkItem->Links.Blink = (PLIST_ENTRY) WorkList->AnchorWorkItem->Links.Blink;
+ WorkItem->Links.Flink->Blink = (PLIST_ENTRY) WorkItem;
+ WorkItem->Links.Blink->Flink = (PLIST_ENTRY) WorkItem;
+
+ WorkList->WorkItemCount++;
+
+LookupAddWorkItemToWorkListFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupAddWorkItemToWorkListError:
+
+ goto LookupAddWorkItemToWorkListFinish;
+}
+
+
+VOID
+LsapDbLookupWorkerThreadStart(
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initiates a child Worker Thread for a Lookup operation.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Start the thread's work processing loop, specifying that this
+ // thread is a child thread rather than the main thread.
+ //
+
+ LsapDbLookupWorkerThread( FALSE );
+}
+
+
+VOID
+LsapDbLookupWorkerThread(
+ IN BOOLEAN PrimaryThread
+ )
+
+/*++
+
+Routine Description:
+
+ This function is executed by each worker thread for a Lookup operation.
+ Each worker thread loops, requesting work items from the Lookup
+ Work Queue. Work Items assigned may belong to any current lookup.
+
+Arguments:
+
+ PrimaryThread - TRUE if thread is the main thread of the Lookup
+ operation, FALSE if the thread is a child thread created by
+ the Lookup operation. The main thread of the Lookup operation
+ also processes work items, but is also responsible for collating
+ the results of the Lookup operation. It is not counted in the
+ active thread count and is not returnable to the thread pool.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
+ PLSAP_DB_LOOKUP_WORK_ITEM WorkItem = NULL;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // If this thread is a child worker thread, increment count of active
+ // child threads.
+ //
+
+ if (!PrimaryThread) {
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupWorkerThreadError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ LookupWorkQueue.ActiveChildThreadCount++;
+
+ LsapDbLookupReleaseWorkQueueLock();
+
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ //
+ // Loop while there is work to do.
+ //
+
+ for (;;) {
+
+ //
+ // Obtain work packet
+ //
+
+ Status = LsapDbLookupObtainWorkItem(&WorkList, &WorkItem);
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsapDbLookupProcessWorkItem(WorkList, WorkItem);
+
+ if (NT_SUCCESS(Status)) {
+
+ continue;
+ }
+
+ //
+ // An error has occurred. Stop this lookup.
+ //
+
+ Status = LsapDbLookupStopProcessingWorkList(WorkList, Status);
+
+ //
+ // NOTE: Intentionally ignore the status.
+ //
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // If an error occurred other than there being no more work to do,
+ // quit.
+ //
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // There is no more work to do. If this thread is a child worker
+ // thread, either return thread to pool and wait for more work, or
+ // terminate if enough threads have already been retained. If this
+ // thread is the main thread of a Lookup operation, just return
+ // in order to collate results.
+ //
+
+ if (!PrimaryThread) {
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ if (LookupWorkQueue.ActiveChildThreadCount <= LookupWorkQueue.MaximumRetainedChildThreadCount) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Wait forever for more work.
+ //
+
+ Status = NtWaitForSingleObject( LsapDbLookupStartedEvent, TRUE, NULL);
+
+ if (NT_SUCCESS(Status)) {
+
+ continue;
+ }
+
+ //
+ // An error occurred in the wait routine. Exit the thread.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+ }
+
+ //
+ // We already have enough active threads or an error has occurred.
+ // Mark this one inactive and terminate it.
+ //
+
+ LookupWorkQueue.ActiveChildThreadCount--;
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Terminate the thread.
+ //
+
+ ExitThread((DWORD) Status);
+ }
+
+ //
+ // We're the Primary Thread of some Lookup operation and there is
+ // no more work to do. Break out so we can return to caller.
+ //
+
+ break;
+ }
+
+LookupWorkerThreadFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return;
+
+LookupWorkerThreadError:
+
+ goto LookupWorkerThreadFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupObtainWorkItem(
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList,
+ OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by a worker thread to obtain a Work Item. This
+ Work Item may belong to any current lookup operation.
+
+Arguments:
+
+ WorkList - Receives a pointer to a Work List structure describing a
+ Lookup Sids or Lookup Names operation.
+
+ WorkItem - Receives a pointer to a Work Item structure describing a
+ list of Sids or Names and a domain in which they are to be looked up.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_NO_MORE_ENTRIES - No more work items available.
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+ *WorkList = NULL;
+ *WorkItem = NULL;
+
+ //
+ // Acquire the Lookup Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupObtainWorkItemError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ //
+ // Return an error if there are no more Work Items.
+ //
+
+ Status = STATUS_NO_MORE_ENTRIES;
+
+ if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
+
+ goto LookupObtainWorkItemError;
+ }
+
+ //
+ // Verify that the Current Assignable Work List does not have
+ // a termination error. This should never happen, because the
+ // pointers should be updated if the Lookup corresponding to the Current
+ // Assignable Work List is terminated.
+ //
+
+ ASSERT(NT_SUCCESS(LookupWorkQueue.CurrentAssignableWorkList->Status));
+
+ //
+ // There are work items available. Check the next one out.
+ //
+
+ ASSERT(LookupWorkQueue.CurrentAssignableWorkItem->State == AssignableWorkItem);
+ LookupWorkQueue.CurrentAssignableWorkItem->State = AssignedWorkItem;
+ *WorkList = LookupWorkQueue.CurrentAssignableWorkList;
+ *WorkItem = LookupWorkQueue.CurrentAssignableWorkItem;
+
+ //
+ // Update pointers to next item (if any) in the current Work List
+ // where work is being given out.
+ //
+
+ Status = LsapDbLookupUpdateAssignableWorkItem(FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupObtainWorkItemError;
+ }
+
+LookupObtainWorkItemFinish:
+
+ //
+ // If we acquired the Lookup Work Queue Lock, release it.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupObtainWorkItemError:
+
+ goto LookupObtainWorkItemFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupProcessWorkItem(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
+ )
+
+/*++
+
+Routine Description:
+
+ This function processes a Work Item for a Lookup operation. The Work
+ Item specifies a number of Sids or Names to be looked up in a given
+ domain.
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a
+ Lookup Sids or Lookup Names operation.
+
+ WorkItem - Pointer to a Work Item structure describing a
+ list of Sids or Names and a domain in which they are to be looked up.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS SecondaryStatus;
+ ULONG NextLevelCount;
+ ULONG NextLevelMappedCount;
+ LSA_HANDLE ControllerPolicyHandle = NULL;
+ ULONG NextLevelIndex;
+ PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
+ PSID *NextLevelSids = NULL;
+ PUNICODE_STRING NextLevelNames = NULL;
+ PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
+ PLSA_TRANSLATED_SID NextLevelTranslatedSids = NULL;
+ PLSA_TRANSLATED_NAME NextLevelTranslatedNames = NULL;
+ ULONG Index;
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("LSA DB: Processing work item. (0x%lx, 0x%lx)\n", WorkList, WorkItem));
+
+ //
+ // Open a Domain Controller for this Trusted Domain.
+ //
+
+ Status = LsapDbOpenPolicyTrustedDomain(
+ &WorkItem->TrustInformation,
+ POLICY_LOOKUP_NAMES,
+ &ControllerPolicyHandle
+ );
+
+ //
+ // If we are unable to connect to the Trusted Domain via any DC,
+ // suppress the error so that the Lookup can continue to try and
+ // translate other Sids/Names.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_SUCCESS;
+ goto LookupProcessWorkItemError;
+ }
+
+ //
+ // Branch according to lookup type.
+ //
+
+ NextLevelCount = WorkItem->UsedCount;
+
+ if (WorkList->LookupType == LookupSids) {
+
+ //
+ // Allocate an array for the Sids to be looked up at a Domain
+ // Controller for the specified Trusted Domain.
+ //
+
+ NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (NextLevelSids == NULL) {
+
+ goto LookupProcessWorkItemError;
+ }
+
+ //
+ // Copy in the Sids to be looked up from the Work List. The Work
+ // Item contains their indices relative to the Sid array in the
+ // Work List.
+ //
+
+ for (NextLevelIndex = 0;
+ NextLevelIndex < NextLevelCount;
+ NextLevelIndex++) {
+
+ Index = WorkItem->Indices[ NextLevelIndex ];
+ NextLevelSids[NextLevelIndex] = WorkList->LookupSidsParams.Sids[Index];
+ }
+
+ Status = STATUS_SUCCESS;
+
+ NextLevelMappedCount = (ULONG) 0;
+
+ //
+ // Lookup the Sids at the DC.
+ //
+
+ Status = LsaICLookupSids(
+ ControllerPolicyHandle,
+ NextLevelCount,
+ NextLevelSids,
+ (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
+ (PLSA_TRANSLATED_NAME *) &NextLevelTranslatedNames,
+ WorkList->LookupLevel,
+ &NextLevelMappedCount
+ );
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("LSA DB: Sid Lookup.\n"
+ " Item: (0x%lx, 0x%lx)\n"
+ " Count: 0x%lx\n", WorkList, WorkItem, NextLevelCount));
+
+ //
+ // If the callout to LsaLookupSids() was unsuccessful, disregard
+ // the error and set the domain name for any Sids having this
+ // domain Sid as prefix sid.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_SUCCESS;
+ goto LookupProcessWorkItemFinish;
+ }
+
+ //
+ // The callout to LsaICLookupSids() was successful. Update the
+ // TranslatedNames information in the Work List as appropriate
+ // using the TranslatedNames information returned from the callout.
+ //
+
+ Status = LsapDbLookupSidsUpdateTranslatedNames(
+ WorkList,
+ WorkItem,
+ NextLevelTranslatedNames
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupProcessWorkItemError;
+ }
+
+ } else if (WorkList->LookupType == LookupNames) {
+
+ //
+ // Allocate an array of UNICODE_STRING structures for the
+ // names to be looked up at the Domain Controller.
+ //
+
+ NextLevelNames = MIDL_user_allocate(sizeof(UNICODE_STRING) * NextLevelCount);
+
+ if (NextLevelNames == NULL) {
+
+ goto LookupProcessWorkItemError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Copy in the Names to be looked up from the Work List. The Work
+ // Item contains their indices relative to the Names array in the
+ // Work List.
+ //
+
+ for (NextLevelIndex = 0;
+ NextLevelIndex < NextLevelCount;
+ NextLevelIndex++) {
+
+ Index = WorkItem->Indices[ NextLevelIndex ];
+ NextLevelNames[NextLevelIndex] =
+ *((PUNICODE_STRING) &WorkList->LookupNamesParams.Names[Index]);
+ }
+
+ NextLevelMappedCount = (ULONG) 0;
+
+ //
+ // Lookup the Names at the DC.
+ //
+
+ Status = LsaICLookupNames(
+ ControllerPolicyHandle,
+ NextLevelCount,
+ NextLevelNames,
+ (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
+ (PLSA_TRANSLATED_SID *) &NextLevelTranslatedSids,
+ WorkList->LookupLevel,
+ &NextLevelMappedCount
+ );
+
+ //
+ // If the callout to LsaLookupNames() was unsuccessful, disregard
+ // the error and set the domain name for any Sids having this
+ // domain Sid as prefix sid.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_SUCCESS;
+ goto LookupProcessWorkItemError;
+ }
+
+ //
+ // The callout to LsaICLookupNames() was successful. Update the
+ // TranslatedSids information in the Work List as appropriate
+ // using the TranslatedSids information returned from the callout.
+ //
+
+ Status = LsapDbLookupNamesUpdateTranslatedSids(
+ WorkList,
+ WorkItem,
+ NextLevelTranslatedSids
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupProcessWorkItemError;
+ }
+
+ } else {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto LookupProcessWorkItemError;
+ }
+
+LookupProcessWorkItemFinish:
+
+ //
+ // Change the state of the work item to "Completed"
+ //
+
+ WorkItem->State = CompletedWorkItem;
+
+
+ //
+ // Update the Mapped Counts
+ //
+
+ LsapDbUpdateMappedCountsWorkList( WorkList );
+
+
+ //
+ // Protect WorkList operations
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+ if (!NT_SUCCESS(Status)) {
+ goto LookupProcessWorkItemError;
+ }
+
+ //
+ // Increment the count of completed Work Items whether or not this
+ // one was completed without error. If the Work List has just been
+ // completed, change its state to "CompletedWorkList" and signal
+ // the Lookup operation completed event. Allow re-entry into
+ // this section if an error is returned.
+ //
+
+ WorkList->CompletedWorkItemCount++;
+
+
+ LsapDiagPrint( DB_LOOKUP_WORK_LIST,
+ ("LSA DB: Process Work Item Completed.\n"
+ " Item: (0x%lx, 0x%lx)\n"
+ " Completed Count: %ld\n", WorkList, WorkItem, WorkList->CompletedWorkItemCount));
+
+ if (WorkList->State != CompletedWorkList) {
+
+ if (WorkList->CompletedWorkItemCount == WorkList->WorkItemCount) {
+
+ WorkList->State = CompletedWorkList;
+
+ SecondaryStatus = LsapDbLookupSignalCompletionWorkList( WorkList );
+ }
+ }
+
+ //
+ // Done making work list changes
+ //
+
+ LsapDbLookupReleaseWorkQueueLock();
+
+
+ //
+ // If necessary, close the Controller Policy Handle.
+ //
+
+ if (ControllerPolicyHandle != NULL) {
+
+ SecondaryStatus = LsaClose( ControllerPolicyHandle );
+ ControllerPolicyHandle = NULL;
+ }
+
+ //
+ // If necessary, free the array of Sids looked up at the next level.
+ //
+
+ if (NextLevelSids != NULL) {
+
+ MIDL_user_free( NextLevelSids );
+ NextLevelSids = NULL;
+ }
+
+ //
+ // If necessary, free the array of Names looked up at the next level.
+ //
+
+ if (NextLevelNames != NULL) {
+ MIDL_user_free( NextLevelNames );
+ NextLevelNames = NULL;
+ }
+
+ if (NextLevelReferencedDomains != NULL) {
+ MIDL_user_free( NextLevelReferencedDomains );
+ NextLevelReferencedDomains = NULL;
+ }
+
+ if (NextLevelTranslatedNames != NULL) {
+ MIDL_user_free( NextLevelTranslatedNames );
+ NextLevelTranslatedNames = NULL;
+ }
+
+ if (NextLevelTranslatedSids != NULL) {
+ MIDL_user_free( NextLevelTranslatedSids );
+ NextLevelTranslatedSids = NULL;
+ }
+
+ return(Status);
+
+LookupProcessWorkItemError:
+
+ goto LookupProcessWorkItemFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupSidsUpdateTranslatedNames(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
+ IN PLSA_TRANSLATED_NAME TranslatedNames
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called during the processing of a Work Item to update
+ the output TranslatedNames and ReferencedDomains parameters of a
+ Lookup operation's Work List with the results of a callout to
+ LsaICLookupNames. Zero or more Sids may have been translated. Note
+ that, unlike the translation of Names, Sid translation occurs within
+ a single Work Item only.
+
+Arguments:
+
+ WorkList - Pointer to a Work List
+
+ WorkItem - Pointer to a Work Item.
+
+ TranslatedNames - Translated Sids information returned from
+ LsaICLookupSids().
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Index, WorkItemIndex;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+ PLSAPR_TRANSLATED_NAME WorkListTranslatedNames =
+ WorkList->LookupSidsParams.TranslatedNames->Names;
+
+ //
+ // Acquire the Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsUpdateTranslatedNamesError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ for( WorkItemIndex = 0;
+ WorkItemIndex < WorkItem->UsedCount;
+ WorkItemIndex++) {
+
+ //
+ // If this Sid has been fully translated, copy information to output.
+ // Note that the Sid is partially translated during the building
+ // phase where its Domain is identified.
+ //
+
+ if (TranslatedNames[WorkItemIndex].Use != SidTypeUnknown) {
+
+ Index = WorkItem->Indices[WorkItemIndex];
+
+ WorkListTranslatedNames[Index].Use
+ = TranslatedNames[WorkItemIndex].Use;
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ (PUNICODE_STRING) &WorkListTranslatedNames[Index].Name,
+ (PUNICODE_STRING) &TranslatedNames[WorkItemIndex].Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupSidsUpdateTranslatedNamesError;
+ }
+
+LookupSidsUpdateTranslatedNamesFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupSidsUpdateTranslatedNamesError:
+
+ goto LookupSidsUpdateTranslatedNamesFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupNamesUpdateTranslatedSids(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
+ IN PLSA_TRANSLATED_SID TranslatedSids
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called during the processing of a Work Item to update
+ the output TranslatedSids and ReferencedDomains parameters of a
+ Lookup operation's Work List with the results of a callout to
+ LsaICLookupNames. Zero or more Names may have been translated, and
+ there is the additional complication of multiple translations of
+ Isolated Names as a result of their presence in more than one
+ Work Item. The following rules apply:
+
+ If the Name is a Qualified Name, it only belongs to the specified
+ Work Item, so it suffices to check that it has been mapped to a Sid.
+
+ If the Name is an Isolated Name, it belongs to all other Work Items,
+ so it may already have been translated during the processing of some
+ other Work Item. If the Name has previously been translated, the prior
+ translation stands and the present translation is discarded. If the
+ Name has not previously been translated, the Domain for this Work Item
+ is added to the Referenced Domain List and the newly obtained translation
+ is stored in the output TranslatedSids array in the Work List.
+
+Arguments:
+
+ WorkList - Pointer to a Work List
+
+ WorkItem - Pointer to a Work Item. The DomainIndex field will be
+ updated if the Domain specified by this Work Item is added to
+ the Referenced Domain List by this routine.
+
+ TranslatedSids - Translated Sids information returned from
+ LsaICLookupNames().
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG WorkItemIndex;
+ ULONG Index;
+ PLSA_TRANSLATED_SID WorkListTranslatedSids =
+ WorkList->LookupNamesParams.TranslatedSids->Sids;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Acquire the Work Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesUpdateTranslatedSidsError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ for( WorkItemIndex = 0;
+ WorkItemIndex < WorkItem->UsedCount;
+ WorkItemIndex++) {
+
+ //
+ // If this Name has not been translated at all during processing of
+ // this Work Item, skip to the next.
+ //
+
+ if (LsapDbCompletelyUnmappedSid(&TranslatedSids[WorkItemIndex])) {
+
+ continue;
+ }
+
+ //
+ // We partially or fully translated the Name during processing of
+ // this Work Item. If this Name has previously been fully translated
+ // during the processing of another Work Item, discard the new
+ // translation and skip to the next Name. Note that Qualified
+ // Names are always partially translated during the building
+ // of the Work List. Isolated Names are fully translated during
+ // the building phase if they are Domain Names.
+ //
+
+ Index = WorkItem->Indices[WorkItemIndex];
+
+ if ( WorkListTranslatedSids[ Index ].Use != SidTypeUnknown ) {
+
+ continue;
+ }
+
+ //
+ // Name has been translated for the first time during the processing
+ // of this Work Item. If this Work Item does not specify a Domain
+ // Index, we need to add its Domain to the Referenced Domains List.
+ //
+
+ if (WorkItem->DomainIndex == LSA_UNKNOWN_INDEX) {
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ WorkList->ReferencedDomains,
+ &WorkItem->TrustInformation,
+ (PLONG) &WorkItem->DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ //
+ // Now update the TranslatedSids array in the Work List.
+
+ WorkListTranslatedSids[Index] = TranslatedSids[WorkItemIndex];
+ WorkListTranslatedSids[Index].DomainIndex = WorkItem->DomainIndex;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupNamesUpdateTranslatedSidsError;
+ }
+
+LookupNamesUpdateTranslatedSidsFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupNamesUpdateTranslatedSidsError:
+
+ goto LookupNamesUpdateTranslatedSidsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupCreateWorkItem(
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN LONG DomainIndex,
+ IN ULONG MaximumEntryCount,
+ OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a new Work Item for a name Lookup operation.
+
+Arguments:
+
+ TrustInformation - Specifies the Name of the Trusted Domain
+ to which the Work Item relates. The Sid field may be NULL or
+ set to the corresponding Sid. The Trust Information is expected
+ to be in heap or global data.
+
+ DomainIndex - Specifies the Domain Index of this domain relative to
+ the Referenced Domain List for the Lookup operation specified
+ by the Work List.
+
+ MaximumEntryCount - Specifies the maximum number of entries that
+ this Work Item will initialiiy be able to contain.
+
+ WorkItem - Receives a pointer to an empty Work Item structure.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_ITEM OutputWorkItem = NULL;
+ PULONG OutputIndices = NULL;
+ ULONG InitialEntryCount;
+
+ //
+ // Allocate memory for the Work Item Header.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ OutputWorkItem = MIDL_user_allocate(sizeof(LSAP_DB_LOOKUP_WORK_ITEM));
+
+ if (OutputWorkItem == NULL) {
+
+ goto LookupCreateWorkItemError;
+ }
+
+ RtlZeroMemory(
+ OutputWorkItem,
+ sizeof(LSAP_DB_LOOKUP_WORK_ITEM)
+ );
+
+ //
+ // Initialize the fixed fields in the Work Item.
+ //
+
+ Status = LsapDbLookupInitializeWorkItem(OutputWorkItem);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupCreateWorkItemError;
+ }
+
+ //
+ // Initialize other fields from parameters.
+ //
+
+ //
+ // Copy the trusted domain information into the work item. The
+ // trust information may be NULL if this is the isolated names
+ // work item.
+ //
+
+ if (TrustInformation != NULL) {
+
+ OutputWorkItem->TrustInformation.Sid =
+ MIDL_user_allocate( RtlLengthSid(TrustInformation->Sid) );
+
+ if (OutputWorkItem->TrustInformation.Sid == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto LookupCreateWorkItemError;
+ }
+
+ RtlCopyMemory(
+ OutputWorkItem->TrustInformation.Sid,
+ TrustInformation->Sid,
+ RtlLengthSid(TrustInformation->Sid)
+ );
+
+ OutputWorkItem->TrustInformation.Name.MaximumLength = TrustInformation->Name.Length + sizeof(WCHAR);
+ OutputWorkItem->TrustInformation.Name.Buffer =
+ MIDL_user_allocate(TrustInformation->Name.Length + sizeof(WCHAR));
+
+ if (OutputWorkItem->TrustInformation.Name.Buffer == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto LookupCreateWorkItemError;
+ }
+
+ RtlCopyUnicodeString(
+ (PUNICODE_STRING) &OutputWorkItem->TrustInformation.Name,
+ (PUNICODE_STRING) &TrustInformation->Name
+ );
+
+ }
+
+
+ //
+ // Create the Indices array in the Work Item.
+ //
+
+ InitialEntryCount = (MaximumEntryCount +
+ LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
+ (~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ OutputIndices = MIDL_user_allocate( InitialEntryCount * sizeof(ULONG) );
+
+ if (OutputIndices == NULL) {
+
+ goto LookupCreateWorkItemError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ OutputWorkItem->UsedCount = (ULONG) 0;
+ OutputWorkItem->MaximumCount = InitialEntryCount;
+ OutputWorkItem->Indices = OutputIndices;
+ OutputWorkItem->DomainIndex = DomainIndex;
+
+LookupCreateWorkItemFinish:
+
+ //
+ // Return pointer to newly created Work Item or NULL.
+ //
+
+ *WorkItem = OutputWorkItem;
+ return(Status);
+
+LookupCreateWorkItemError:
+
+ //
+ // Free memory allocated for Indices array.
+ //
+
+ if (OutputIndices != NULL) {
+
+ MIDL_user_free( OutputIndices );
+ OutputIndices = NULL;
+ }
+
+ //
+ // Free any memory allocated for the Work Item header.
+ //
+
+ if (OutputWorkItem != NULL) {
+ if (OutputWorkItem->TrustInformation.Sid != NULL) {
+ MIDL_user_free( OutputWorkItem->TrustInformation.Sid );
+ }
+
+ if (OutputWorkItem->TrustInformation.Name.Buffer != NULL) {
+ MIDL_user_free( OutputWorkItem->TrustInformation.Name.Buffer );
+ }
+
+ MIDL_user_free(OutputWorkItem);
+ OutputWorkItem = NULL;
+ }
+
+ goto LookupCreateWorkItemFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupAddIndicesToWorkItem(
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
+ IN ULONG Count,
+ IN PULONG Indices
+ )
+
+/*++
+
+Routine Description:
+
+ This function adds an array of Sid or Name indices to a Work Item.
+ The indices specify Sids or Names within the Sids or Names arrays in
+ the WorkList. If there is sufficient room in the Work Item's
+ existing indices array, the indices will be copied to that array.
+ Otherwise, a larger array will be allocated and the existing one
+ will be freed after copying the existing indices.
+
+ NOTE: The Work Item must NOT belong to a Work List that is currently
+ on the Work Queue. The Lookup Work Queue Lock will not be
+ taken.
+Arguments:
+
+ WorkItem - Pointer to a Work Item structure describing a
+ list of Sids or Names and a domain in which they are to be looked up.
+
+ Count - Specifies the number of indices to be added.
+
+ Indices - Specifies the array of indices to be added to the WorkItem.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PULONG OutputIndices = NULL;
+ ULONG NewMaximumCount;
+
+ //
+ // Check room available in the work item. If there is enough
+ // room, just copy the indices in.
+ //
+
+ if (WorkItem->MaximumCount - WorkItem->UsedCount >= Count) {
+
+ RtlCopyMemory(
+ &WorkItem->Indices[WorkItem->UsedCount],
+ Indices,
+ Count * sizeof(ULONG)
+ );
+
+ WorkItem->UsedCount += Count;
+ goto AddIndicesToWorkItemFinish;
+ }
+
+ //
+ // Allocate array of sufficient size to accommodate the existing
+ // and new indices. Round up number of entries to some granularity
+ // to avoid frequent reallocations.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ NewMaximumCount = ((WorkItem->UsedCount + Count) +
+ LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
+ (~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);
+
+ OutputIndices = MIDL_user_allocate( NewMaximumCount * sizeof(ULONG) );
+
+ if (OutputIndices == NULL) {
+
+ goto AddIndicesToWorkItemError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Copy in the existing and new indices.
+ //
+
+ RtlCopyMemory(
+ OutputIndices,
+ WorkItem->Indices,
+ WorkItem->UsedCount * sizeof(ULONG)
+ );
+
+ RtlCopyMemory(
+ &OutputIndices[WorkItem->UsedCount],
+ Indices,
+ Count * sizeof(ULONG)
+ );
+
+ //
+ // Free the existing indices. Set pointer to the updated indices array
+ // and update the used and maximum counts.
+ //
+
+ MIDL_user_free( WorkItem->Indices );
+ WorkItem->Indices = OutputIndices;
+ WorkItem->UsedCount += Count;
+ WorkItem->MaximumCount = NewMaximumCount;
+
+AddIndicesToWorkItemFinish:
+
+ return(Status);
+
+AddIndicesToWorkItemError:
+
+ //
+ // Free any memory allocated for the Output Indices array.
+ //
+
+ if (OutputIndices != NULL) {
+
+ MIDL_user_free( OutputIndices );
+ OutputIndices = NULL;
+ }
+
+ goto AddIndicesToWorkItemFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupComputeAdvisoryChildThreadCount(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function computes the advisory thread count for a Lookup
+ operation. This count is an estimate of the optimal number of
+ worker threads (in addition to the main thread) needed to process the
+ Work List.
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a
+ Lookup Sids or Lookup Names operation.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ ASSERT(WorkList->WorkItemCount != (ULONG) 0);
+ WorkList->AdvisoryChildThreadCount = (WorkList->WorkItemCount - (ULONG) 1);
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbLookupUpdateAssignableWorkItem(
+ IN BOOLEAN MoveToNextWorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function updates the next assignable Work Item pointers.
+
+Arguments:
+
+ MoveToNextWorkList - If TRUE, skip the rest of the current Work List. If
+ FALSE, point at the next item in the current Work List.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_ITEM CandAssignableWorkItem = NULL;
+ PLSAP_DB_LOOKUP_WORK_LIST CandAssignableWorkList = NULL;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Acquire the LookupWork Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupUpdateAssignableWorkItemError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ //
+ // If there is no Currently Assignable Work List, just exit.
+ //
+
+ if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
+
+ goto LookupUpdateAssignableWorkItemFinish;
+ }
+
+ //
+ // There is a Currently Assignable Work List. Unless requested to
+ // skip this Work List, examine it.
+ //
+
+ if (!MoveToNextWorkList) {
+
+ ASSERT( LookupWorkQueue.CurrentAssignableWorkItem != NULL);
+
+ //
+ // Select the next Work Item in the list as candidate for the
+ // next Assignable Work Item. If we have not returned to the First
+ // Work Item, selection is complete.
+ //
+
+ CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
+ LookupWorkQueue.CurrentAssignableWorkItem->Links.Flink;
+
+ if (CandAssignableWorkItem !=
+ LookupWorkQueue.CurrentAssignableWorkList->AnchorWorkItem) {
+
+ ASSERT( CandAssignableWorkItem->State == AssignableWorkItem);
+
+ LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
+ goto LookupUpdateAssignableWorkItemFinish;
+ }
+ }
+
+ //
+ // There are no more work items in this Work List or we're to skip the
+ // rest of it. See if there is another Work List.
+ //
+
+ CandAssignableWorkList = (PLSAP_DB_LOOKUP_WORK_LIST)
+ LookupWorkQueue.CurrentAssignableWorkList->WorkLists.Flink;
+
+ if (CandAssignableWorkList != LookupWorkQueue.AnchorWorkList) {
+
+ //
+ // There is another Work List. Select the first Work Item in the
+ // list following the anchor.
+ //
+
+ CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
+ CandAssignableWorkList->AnchorWorkItem->Links.Flink;
+
+ //
+ // Verify that the list does not just contain the Anchor Work Item.
+ // Work Lists on the Work Queue should never be empty.
+ //
+
+ ASSERT (CandAssignableWorkItem != CandAssignableWorkList->AnchorWorkItem);
+
+ LookupWorkQueue.CurrentAssignableWorkList = CandAssignableWorkList;
+ LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
+ goto LookupUpdateAssignableWorkItemFinish;
+ }
+
+ //
+ // All work has been assigned. Set pointers to NULL.
+ //
+
+ LookupWorkQueue.CurrentAssignableWorkList = NULL;
+ LookupWorkQueue.CurrentAssignableWorkItem = NULL;
+
+LookupUpdateAssignableWorkItemFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupUpdateAssignableWorkItemError:
+
+ goto LookupUpdateAssignableWorkItemFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupStopProcessingWorkList(
+ IN PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN NTSTATUS TerminationStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function stops further work on a lookup operation at a given
+ level and stores an error code.
+
+Arguments:
+
+ WorkList - Pointer to a Work List structure describing a
+ Lookup Sids or Lookup Names operation.
+
+ TerminationStatus - Specifies the Nt Result Code to be returned
+ by LsarLookupnames or LsarLookupSids.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN AcquiredWorkQueueLock = FALSE;
+
+ //
+ // Acquire the LookupWork Queue Lock.
+ //
+
+ Status = LsapDbLookupAcquireWorkQueueLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupStopProcessingWorkListError;
+ }
+
+ AcquiredWorkQueueLock = TRUE;
+
+ //
+ // Store the termination status in the appropriate WorkList.
+ //
+
+ WorkList->Status = TerminationStatus;
+
+ //
+ // If this WorkList happens to be the one in which Work Items are being
+ // given out, we need to prevent any further Work Items from being given
+ // out. Update the next assignable Work Item pointers, specifying that
+ // we should skip to the next Work List (if any).
+ //
+
+ if (WorkList == LookupWorkQueue.CurrentAssignableWorkList) {
+
+ Status = LsapDbLookupUpdateAssignableWorkItem(TRUE);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupStopProcessingWorkListError;
+ }
+
+LookupStopProcessingWorkListFinish:
+
+ //
+ // If necessary, release the Lookup Work Queue Lock.
+ //
+
+ if (AcquiredWorkQueueLock) {
+
+ LsapDbLookupReleaseWorkQueueLock();
+ AcquiredWorkQueueLock = FALSE;
+ }
+
+ return(Status);
+
+LookupStopProcessingWorkListError:
+
+ goto LookupStopProcessingWorkListFinish;
+}
+
+
+NTSTATUS
+LsapRtlExtractDomainSid(
+ IN PSID Sid,
+ OUT PSID *DomainSid
+ )
+
+/*++
+
+Routine Description:
+
+ This function extracts a Domain Sid from a Sid.
+
+Arguments:
+
+ Sid - Pointer to Sid whose Domain Prefix Sid is to be extracted.
+
+ DomainSid - Receives pointer to Domain Sid. This Sid will be
+ allocated memory by MIDL_User_allocate() and should be freed
+ via MIDL_user_free when no longer required.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+--*/
+
+{
+ PSID OutputDomainSid;
+ ULONG DomainSidLength = RtlLengthSid(Sid) - sizeof(ULONG);
+
+
+
+ OutputDomainSid = MIDL_user_allocate( DomainSidLength );
+ if (OutputDomainSid == NULL) {
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+
+ RtlCopyMemory( OutputDomainSid, Sid, DomainSidLength);
+ (*(RtlSubAuthorityCountSid(OutputDomainSid)))--;
+
+ *DomainSid = OutputDomainSid;
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+NTSTATUS
+LsapDbLookupInitialize(
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs initialization of the data structures
+ used by Lookup operations. These structures are as follows:
+
+ LookupWorkQueue - This is a doubly-linked list of Lookup Work Lists.
+ There is one Work List for each Lookup operation in progress
+ on a DC. Each Lookup Work List contains a doubly-linked list
+ of Lookup Work Items. Each Lookup Work Item specifies a
+ Trusted Domain and array of Sids or Names to be looked up in that
+ domain. Access to this queue is controlled via the Lookup
+ Work Queue Lock.
+
+ Trusted Domain List - This is a doubly-linked list which contains
+ the Trust Information (i.e. Domain Sid and Domain Name) of
+ each Trusted Domain. The purpose of this list is to enable
+ fast identification of Trusted Domain Sids and Names, without
+ having recourse to open or enumerate Trusted Domain objects.
+ This list is initialized when the system is loaded, and is
+ updated directly when a Trusted Domain object is created or
+ deleted.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Perform initialization specific to DC's
+ //
+
+ if (LsapProductType != NtProductLanManNt) {
+
+ goto LookupInitializeFinish;
+ }
+
+ //
+ // Create the Lookup Work List initiated event.
+ //
+
+ Status = NtCreateEvent(
+ &LsapDbLookupStartedEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupInitializeError;
+ }
+
+ //
+ // Create the Lookup Work List complete event.
+ //
+
+ Status = NtCreateEvent(
+ &LsapDbLookupCompleteEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupInitializeError;
+ }
+
+ //
+ // Initialize the Lookup Work Queue
+ //
+
+ Status = LsapDbLookupInitializeWorkQueue();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupInitializeError;
+ }
+
+LookupInitializeFinish:
+
+ return(Status);
+
+LookupInitializeError:
+
+ goto LookupInitializeFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupInitializeWorkQueue(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the Lookup Work Queue. It is only
+ called for DC's.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_LIST AnchorWorkList = NULL;
+
+ //
+ // Initialize the Work Queue Lock.
+ //
+
+ Status = RtlInitializeCriticalSection(&LookupWorkQueue.Lock);
+
+ if (!NT_SUCCESS(Status)) {
+
+ LsapLogError(
+ "LsapDbLookupInitialize: RtlInit..CritSec returned 0x%lx\n",
+ Status
+ );
+ }
+
+ //
+ // Initialize the Work Queue to comprise the Anchor Work List
+ // doubly-linked to itself.
+ //
+
+ LookupWorkQueue.AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;
+ AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;
+
+ //
+ // Set the currently assignable Work List and Work Item pointers to
+ // NULL to indicate that there is no work to to.
+ //
+
+ LookupWorkQueue.CurrentAssignableWorkList = NULL;
+ LookupWorkQueue.CurrentAssignableWorkItem = NULL;
+
+ //
+ // Initialize the Anchor Work List.
+ //
+
+ Status = LsapDbLookupInitializeWorkList(AnchorWorkList);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupInitializeWorkQueueError;
+ }
+
+ AnchorWorkList->WorkLists.Flink = (PLIST_ENTRY) AnchorWorkList;
+ AnchorWorkList->WorkLists.Blink = (PLIST_ENTRY) AnchorWorkList;
+
+ //
+ // Set the thread counts.
+ //
+
+ LookupWorkQueue.ActiveChildThreadCount = (ULONG) 0;
+ LookupWorkQueue.MaximumChildThreadCount = LSAP_DB_LOOKUP_MAX_THREAD_COUNT;
+ LookupWorkQueue.MaximumRetainedChildThreadCount = LSAP_DB_LOOKUP_MAX_RET_THREAD_COUNT;
+
+LookupInitializeWorkQueueFinish:
+
+ return(Status);
+
+LookupInitializeWorkQueueError:
+
+ goto LookupInitializeWorkQueueFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupInitializeWorkList(
+ OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes an empty Work List structure. The Work List
+ link fields are not set by this function.
+
+Arguments:
+
+ WorkList - Points to Work List structure to be initialized.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
+
+ //
+ // Initialize miscellaneous fields in the Work List header.
+ //
+
+ WorkList->WorkLists.Flink = NULL;
+ WorkList->WorkLists.Blink = NULL;
+ WorkList->WorkItemCount = (ULONG) 0;
+ WorkList->CompletedWorkItemCount = (ULONG) 0;
+ WorkList->State = InactiveWorkList;
+ WorkList->Status = STATUS_SUCCESS;
+
+ //
+ // Initialize the Work List's list of Work Items to comprise the
+ // Anchor Work Item doubly-linked to itself.
+ //
+
+ WorkList->AnchorWorkItem = &WorkList->DummyAnchorWorkItem;
+ AnchorWorkItem = WorkList->AnchorWorkItem;
+ AnchorWorkItem->Links.Flink = (PLIST_ENTRY) AnchorWorkItem;
+ AnchorWorkItem->Links.Blink = (PLIST_ENTRY) AnchorWorkItem;
+
+ //
+ // Initialize the Anchor Work Item.
+ //
+
+ Status = LsapDbLookupInitializeWorkItem(AnchorWorkItem);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupInitializeWorkListError;
+ }
+
+LookupInitializeWorkListFinish:
+
+ return(Status);
+
+LookupInitializeWorkListError:
+
+ goto LookupInitializeWorkListFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupInitializeWorkItem(
+ OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes an empty Work Item structure. The Work Item
+ link fields are not set by this function.
+
+Arguments:
+
+ WorkItem - Points to Work Item structure to be initialized.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ WorkItem->UsedCount = (ULONG) 0;
+ WorkItem->MaximumCount = (ULONG) 0;
+ WorkItem->State = NonAssignableWorkItem;
+ WorkItem->Properties = (ULONG) 0;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbLookupLocalDomains(
+ OUT PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ OUT PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ OUT PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns Trust Information for the Local Domains.
+
+Arguments:
+
+ BuiltInDomainTrustInformation - Pointer to structure that will
+ receive the Name and Sid of the Built-In Domain. Unlike
+ the other two parameters, the Name and Sid buffers for the
+ Built-in Domain are NOT freed after use, because they are
+ global data constants.
+
+ AccountDomainTrustInformation - Pointer to structure that will
+ receive the Name and Sid of the Accounts Domain. The Name and
+ Sid buffers must be freed after use via MIDL_user_free.
+
+ PrimaryDomainTrustInformation - Pointer to structure that will
+ receive the Name and Sid of the Accounts Domain. The Name and
+ Sid buffers must be freed after use via MIDL_user_free.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG WellKnownSidIndex;
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
+ PLSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo = NULL;
+
+ //
+ // Obtain the Name and Sid of the Built-in Domain
+ //
+
+ BuiltInDomainTrustInformation->Sid = LsapBuiltInDomainSid;
+
+ Status = STATUS_INTERNAL_DB_ERROR;
+
+ if (!LsapDbLookupIndexWellKnownSid(
+ LsapBuiltInDomainSid,
+ (PLSAP_WELL_KNOWN_SID_INDEX) &WellKnownSidIndex
+ )) {
+
+ goto LookupLocalDomainsError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Obtain the name of the Built In Domain from the table of
+ // Well Known Sids. It suffices to copy the Unicode structures
+ // since we do not need a separate copy of the name buffer.
+ //
+
+ BuiltInDomainTrustInformation->Name = *((PLSAPR_UNICODE_STRING)
+ LsapDbWellKnownSidName(WellKnownSidIndex));
+
+ //
+ // Now obtain the Name and Sid of the Account Domain.
+ // The Sid and Name of the Account Domain are both configurable, and
+ // we need to obtain them from the Policy Object. Now obtain the
+ // Account Domain Sid and Name by querying the appropriate
+ // Policy Information Class.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupLocalDomainsError;
+ }
+
+ //
+ // Set up the Trust Information structure for the Account Domain.
+ //
+
+ AccountDomainTrustInformation->Sid = PolicyAccountDomainInfo->DomainSid;
+
+ RtlCopyMemory(
+ &AccountDomainTrustInformation->Name,
+ &PolicyAccountDomainInfo->DomainName,
+ sizeof (UNICODE_STRING)
+ );
+
+ //
+ // Now obtain the Name and Sid of the Primary Domain (if any)
+ // The Sid and Name of the Primary Domain are both configurable, and
+ // we need to obtain them from the Policy Object. Now obtain the
+ // Account Domain Sid and Name by querying the appropriate
+ // Policy Information Class.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupLocalDomainsError;
+ }
+
+ //
+ // Set up the Trust Information structure for the Primary Domain.
+ //
+
+ PrimaryDomainTrustInformation->Sid = PolicyPrimaryDomainInfo->Sid;
+
+ RtlCopyMemory(
+ &PrimaryDomainTrustInformation->Name,
+ &PolicyPrimaryDomainInfo->Name,
+ sizeof (UNICODE_STRING)
+ );
+
+LookupLocalDomainsFinish:
+
+ //
+ // If allocated, free up the top-level structures returned by
+ // LsarQueryInformationPolicy.
+ //
+
+ if (PolicyPrimaryDomainInfo != NULL) {
+
+ MIDL_user_free(PolicyPrimaryDomainInfo);
+ }
+
+ if (PolicyAccountDomainInfo != NULL) {
+
+ MIDL_user_free(PolicyAccountDomainInfo);
+ }
+
+ return(Status);
+
+LookupLocalDomainsError:
+
+ goto LookupLocalDomainsFinish;
+}
+
+
+NTSTATUS
+LsapDbLookupIsolatedDomainName(
+ IN ULONG NameIndex,
+ IN PLSAPR_UNICODE_STRING IsolatedName,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates an Isolated Name if it matches a
+ given Domain Name.
+
+Arguments:
+
+ NameIndex - Specifies the index of the entry for the Name within
+ the TranslatedSids array, which will be updated if the Name
+ matches the Domain Name contained in the TrusteInformation parameter.
+
+ IsolatedName - Specifies the Name to be compared with the Domain Name
+ contained in the TrustInformation parameter.
+
+ TrustInformation - Specifies the Name and Sid of a Domain.
+
+ MappedIsolatedNameCount - Pointer to a location in which a count
+ of the Isolated Names that have been mapped is maintained.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_NONE_MAPPED - The specified name is not the same as the
+ name of the specified domain.
+
+ Result codes from called routines.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_NONE_MAPPED;
+
+ //
+ // See if the names match. If they don't, return error.
+ //
+
+ if (!RtlEqualDomainName(
+ (PUNICODE_STRING) IsolatedName,
+ (PUNICODE_STRING) &TrustInformation->Name
+ )) {
+
+ goto LookupIsolatedDomainNameError;
+ }
+
+ //
+ // Name matches the name of the given Domain. Add that
+ // Domain to the Referenced Domain List and translate it.
+ //
+
+ Status = LsapDbLookupAddListReferencedDomains(
+ ReferencedDomains,
+ TrustInformation,
+ (PLONG) &TranslatedSids->Sids[NameIndex].DomainIndex
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LookupIsolatedDomainNameError;
+ }
+
+ //
+ // Fill in the Translated Sids entry.
+ //
+
+ TranslatedSids->Sids[NameIndex].Use = SidTypeDomain;
+ TranslatedSids->Sids[NameIndex].RelativeId = LSA_UNKNOWN_ID;
+
+ Status = STATUS_SUCCESS;
+ (*MappedCount)++;
+ (*CompletelyUnmappedCount)--;
+ (*MappedIsolatedNameCount)++;
+
+LookupIsolatedDomainNameFinish:
+
+ return(Status);
+
+LookupIsolatedDomainNameError:
+
+ goto LookupIsolatedDomainNameFinish;
+}
+
+NTSTATUS
+LsarGetUserName(
+ IN PLSAPR_SERVER_NAME ServerName,
+ OUT PLSAPR_UNICODE_STRING * UserName,
+ OUT OPTIONAL PLSAPR_UNICODE_STRING * DomainName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the LSA Server worker routine for the LsaGetUserName
+ API.
+
+
+ WARNING: This routine allocates memory for its output. The caller is
+ responsible for freeing this memory after use. See description of the
+ Names parameter.
+
+Arguments:
+
+ ServerName - the name of the server the client asked to execute
+ this API on, or NULL for the local machine.
+
+ UserName - Receives name of the current user.
+
+ DomainName - Optioanlly receives domain name of the current user.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully and all Sids have
+ been translated to names.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory to complete the call.
+--*/
+
+{
+ LUID LogonId;
+ PUNICODE_STRING AccountName;
+ PUNICODE_STRING AuthorityName;
+ PSID UserSid;
+ PSID DomainSid = NULL;
+ ULONG Rid;
+ PLSAP_LOGON_SESSION LogonSession;
+ PTOKEN_USER TokenUserInformation = NULL;
+ NTSTATUS Status;
+ BOOLEAN LockHeld = FALSE;
+
+ *UserName = NULL;
+ if (ARGUMENT_PRESENT(DomainName)) {
+ *DomainName = NULL;
+ }
+
+ //
+ // Let's see if we're trying to look up the currently logged on
+ // user.
+ //
+ //
+ // TokenUserInformation from this call must be freed by calling
+ // LsapFreeLsaHeap().
+ //
+
+ Status = LsapQueryClientInfo(
+ &TokenUserInformation,
+ &LogonId
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ goto Cleanup;
+ }
+
+ //
+ // If the user ID is Anonymous then there is no name and domain in the
+ // logon session
+ //
+
+ if (RtlEqualSid(
+ TokenUserInformation->User.Sid,
+ LsapAnonymousSid
+ )) {
+ AccountName = &WellKnownSids[LsapAnonymousSidIndex].Name;
+ AuthorityName = &WellKnownSids[LsapAnonymousSidIndex].DomainName;
+
+ } else {
+
+ LsapAuLock();
+ LockHeld = TRUE;
+
+ LogonSession = LsapGetLogonSession ( &LogonId, FALSE );
+
+ //
+ // During setup, we may get NULL returned for the logon session.
+ //
+
+ if (LogonSession == NULL) {
+
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+ goto Cleanup;
+ }
+
+ //
+ // Got a match. Get the username and domain information
+ // from the LogonId
+ //
+
+
+ AccountName = LogonSession->AccountName;
+ AuthorityName = LogonSession->AuthorityName;
+
+ }
+
+ *UserName = MIDL_user_allocate(sizeof(LSAPR_UNICODE_STRING));
+
+ if (*UserName == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ (PUNICODE_STRING) *UserName,
+ AccountName
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Optionally copy the domain name
+ //
+
+ if (ARGUMENT_PRESENT(DomainName)) {
+
+ *DomainName = MIDL_user_allocate(sizeof(LSAPR_UNICODE_STRING));
+
+ if (*DomainName == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ (PUNICODE_STRING) *DomainName,
+ AuthorityName
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ }
+
+
+
+
+Cleanup:
+
+ if (LockHeld) {
+ LsapAuUnlock();
+ }
+
+ if (TokenUserInformation != NULL) {
+ LsapFreeLsaHeap( TokenUserInformation );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ if (*UserName != NULL) {
+ if ((*UserName)->Buffer != NULL) {
+ MIDL_user_free((*UserName)->Buffer);
+ }
+ MIDL_user_free(*UserName);
+ *UserName = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT(DomainName) ){
+ if (*DomainName != NULL) {
+ if ((*DomainName)->Buffer != NULL) {
+ MIDL_user_free((*DomainName)->Buffer);
+ }
+ MIDL_user_free(*DomainName);
+ *DomainName = NULL;
+ }
+ }
+ }
+ return(Status);
+}
+
diff --git a/private/lsa/server/dblookup.h b/private/lsa/server/dblookup.h
new file mode 100644
index 000000000..ed7eb65e5
--- /dev/null
+++ b/private/lsa/server/dblookup.h
@@ -0,0 +1,915 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ dblookup.h
+
+Abstract:
+
+ LSA Database - Lookup Sid and Name Routine Private Data Definitions.
+
+ NOTE: This module should remain as portable code that is independent
+ of the implementation of the LSA Database. As such, it is
+ permitted to use only the exported LSA Database interfaces
+ contained in db.h and NOT the private implementation
+ dependent functions in dbp.h.
+
+Author:
+
+ Scott Birrell (ScottBi) Novwember 27, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+//////////////////////////////////////////////////////////////////////////
+// //
+// Private Datatypes and Defines //
+// //
+//////////////////////////////////////////////////////////////////////////
+
+//
+// Maximum number of Lookup Threads and maximum number to retain.
+//
+
+#define LSAP_DB_LOOKUP_MAX_THREAD_COUNT ((ULONG) 0x00000010)
+#define LSAP_DB_LOOKUP_MAX_RET_THREAD_COUNT ((ULONG) 0x00000004)
+
+//
+// Work Item Granularity.
+//
+
+#define LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY ((ULONG) 0x0000000f)
+
+//
+// Parameters specific to a Lookup Sids call.
+//
+
+typedef struct _LSAP_DB_LOOKUP_SIDS_PARAMS {
+
+ PLSAPR_SID *Sids;
+ PLSAPR_TRANSLATED_NAMES TranslatedNames;
+
+} LSAP_DB_LOOKUP_SIDS_PARAMS, *PLSAP_DB_LOOKUP_SIDS_PARAMS;
+
+//
+// Parameters specific to a Lookup Names call.
+//
+
+typedef struct _LSAP_DB_LOOKUP_NAMES_PARAMS {
+
+ PLSAPR_UNICODE_STRING Names;
+ PLSAPR_TRANSLATED_SIDS TranslatedSids;
+
+} LSAP_DB_LOOKUP_NAMES_PARAMS, *PLSAP_DB_LOOKUP_NAMES_PARAMS;
+
+//
+// Types of Lookup Operation.
+//
+
+typedef enum {
+
+ LookupSids = 1,
+ LookupNames
+
+} LSAP_DB_LOOKUP_TYPE, *PLSAP_DB_LOOKUP_TYPE;
+
+//
+// Work Item states - Assignable, Assigned, Completed, Reassign
+//
+
+typedef enum {
+
+ AssignableWorkItem = 1,
+ AssignedWorkItem,
+ CompletedWorkItem,
+ ReassignWorkItem,
+ NonAssignableWorkItem
+
+} LSAP_DB_LOOKUP_WORK_ITEM_STATE, *PLSAP_DB_LOOKUP_WORK_ITEM_STATE;
+
+//
+// Work Item Properties.
+//
+
+#define LSAP_DB_LOOKUP_WORK_ITEM_ISOL ((ULONG) 0x00000001L)
+
+//
+// Lookup Work Item. Each work item specifies a domain and an array of
+// Sids or Names to be looked up in that domain. This array is specified
+// as an array of the Sid or Name indices relevant to the arrays specified
+// as parameters to the lookup call.
+//
+
+typedef struct _LSAP_DB_LOOKUP_WORK_ITEM {
+
+ LIST_ENTRY Links;
+ LSAP_DB_LOOKUP_WORK_ITEM_STATE State;
+ ULONG Properties;
+ LSAPR_TRUST_INFORMATION TrustInformation;
+ LONG DomainIndex;
+ ULONG UsedCount;
+ ULONG MaximumCount;
+ PULONG Indices;
+
+} LSAP_DB_LOOKUP_WORK_ITEM, *PLSAP_DB_LOOKUP_WORK_ITEM;
+
+//
+// Lookup Work List State.
+//
+
+typedef enum {
+
+ InactiveWorkList = 1,
+ ActiveWorkList,
+ CompletedWorkList
+
+} LSAP_DB_LOOKUP_WORK_LIST_STATE, *PLSAP_DB_LOOKUP_WORK_LIST_STATE;
+
+//
+// Work List for a Lookup Operation. These are linked together if
+// concurrent lookups are permitted.
+//
+
+typedef struct _LSAP_DB_LOOKUP_WORK_LIST {
+
+ LIST_ENTRY WorkLists;
+ PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem;
+ NTSTATUS Status;
+ LSAP_DB_LOOKUP_WORK_LIST_STATE State;
+ LSAP_DB_LOOKUP_TYPE LookupType;
+ LSAPR_HANDLE PolicyHandle;
+ ULONG WorkItemCount;
+ ULONG CompletedWorkItemCount;
+ ULONG Count;
+ LSAP_LOOKUP_LEVEL LookupLevel;
+ PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains;
+ PULONG MappedCount;
+ PULONG CompletelyUnmappedCount;
+ ULONG AdvisoryChildThreadCount;
+
+ union {
+
+ LSAP_DB_LOOKUP_SIDS_PARAMS LookupSidsParams;
+ LSAP_DB_LOOKUP_NAMES_PARAMS LookupNamesParams;
+ };
+
+ LSAP_DB_LOOKUP_WORK_ITEM DummyAnchorWorkItem;
+
+} LSAP_DB_LOOKUP_WORK_LIST, *PLSAP_DB_LOOKUP_WORK_LIST;
+
+//
+// Lookup Operation Work Queue. The Queue is a circular doubly linked
+// list of Work Lists. Each Work List corresponds to a single
+// Lookup Operation (i.e. an LsarLookupSids or LsarLookupNames call).
+// A Work List is a circular doubly linked list of Work Items, each
+// of these being a list of Sids or Names belonging to a specific
+// Trusted Domain. Work Items can be given out to different threads.
+//
+
+typedef struct _LSAP_DB_LOOKUP_WORK_QUEUE {
+
+ RTL_CRITICAL_SECTION Lock;
+ PLSAP_DB_LOOKUP_WORK_LIST AnchorWorkList;
+ PLSAP_DB_LOOKUP_WORK_LIST CurrentAssignableWorkList;
+ PLSAP_DB_LOOKUP_WORK_ITEM CurrentAssignableWorkItem;
+ ULONG ActiveChildThreadCount;
+ ULONG MaximumChildThreadCount;
+ ULONG MaximumRetainedChildThreadCount;
+ LSAP_DB_LOOKUP_WORK_LIST DummyAnchorWorkList;
+
+} LSAP_DB_LOOKUP_WORK_QUEUE, *PLSAP_DB_LOOKUP_WORK_QUEUE;
+
+static LSAP_DB_LOOKUP_WORK_QUEUE LookupWorkQueue;
+
+
+//
+// Index to table of the well known SIDs
+//
+// This type indexes the table of well-known Sids maintained by the LSA
+//
+
+typedef enum _LSAP_WELL_KNOWN_SID_INDEX {
+
+ LsapNullSidIndex = 0,
+ LsapWorldSidIndex,
+ LsapLocalSidIndex,
+ LsapCreatorOwnerSidIndex,
+ LsapCreatorGroupSidIndex,
+ LsapCreatorOwnerServerSidIndex,
+ LsapCreatorGroupServerSidIndex,
+ LsapNtAuthoritySidIndex,
+ LsapDialupSidIndex,
+ LsapNetworkSidIndex,
+ LsapBatchSidIndex,
+ LsapInteractiveSidIndex,
+ LsapServiceSidIndex,
+ LsapLogonSidIndex,
+ LsapBuiltInDomainSidIndex,
+ LsapLocalSystemSidIndex,
+ LsapAliasAdminsSidIndex,
+ LsapAnonymousSidIndex,
+ LsapServerSidIndex,
+ LsapDummyLastSidIndex
+
+} LSAP_WELL_KNOWN_SID_INDEX, *PLSAP_WELL_KNOWN_SID_INDEX;
+
+
+//
+// Mnemonics for Universal well known SIDs. These reference the corresponding
+// entries in the Well Known Sids table.
+//
+
+#define LsapNullSid WellKnownSids[LsapNullSidIndex].Sid
+#define LsapWorldSid WellKnownSids[LsapWorldSidIndex].Sid
+#define LsapLocalSid WellKnownSids[LsapLocalSidIndex].Sid
+#define LsapCreatorOwnerSid WellKnownSids[LsapCreatorOwnerSidIndex].Sid
+#define LsapCreatorGroupSid WellKnownSids[LsapCreatorGroupSidIndex].Sid
+#define LsapCreatorOwnerServerSid WellKnownSids[LsapCreatorOwnerServerSidIndex].Sid
+#define LsapCreatorGroupServerSid WellKnownSids[LsapCreatorGroupServerSidIndex].Sid
+
+//
+// Sids defined by NT
+//
+
+#define LsapNtAuthoritySid WellKnownSids[LsapNtAuthoritySid].Sid
+
+#define LsapDialupSid WellKnownSids[LsapDialupSidIndex].Sid
+#define LsapNetworkSid WellKnownSids[LsapNetworkSidIndex].Sid
+#define LsapBatchSid WellKnownSids[LsapBatchSidIndex].Sid
+#define LsapInteractiveSid WellKnownSids[LsapInteractiveSidIndex].Sid
+#define LsapServiceSid WellKnownSids[LsapServiceSidIndex].Sid
+#define LsapBuiltInDomainSid WellKnownSids[LsapBuiltInDomainSidIndex].Sid
+#define LsapLocalSystemSid WellKnownSids[LsapLocalSystemSidIndex].Sid
+
+#define LsapAliasAdminsSid WellKnownSids[LsapAliasAdminsSidIndex].Sid
+
+#define LsapAnonymousSid WellKnownSids[LsapAnonymousSidIndex].Sid
+#define LsapServerSid WellKnownSids[LsapServerSidIndex].Sid
+
+//
+// Well known LUIDs
+//
+
+LUID LsapSystemLogonId;
+
+
+
+//
+// Well known privilege values
+//
+
+
+LUID LsapCreateTokenPrivilege;
+LUID LsapAssignPrimaryTokenPrivilege;
+LUID LsapLockMemoryPrivilege;
+LUID LsapIncreaseQuotaPrivilege;
+LUID LsapUnsolicitedInputPrivilege;
+LUID LsapTcbPrivilege;
+LUID LsapSecurityPrivilege;
+LUID LsapTakeOwnershipPrivilege;
+
+static SID_IDENTIFIER_AUTHORITY LsapNullSidAuthority = SECURITY_NULL_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY LsapWorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY LsapLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY LsapCreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY LsapNtAuthority
+ = SECURITY_NT_AUTHORITY;
+
+//
+// Maximum number of Subauthority levels for well known Sids
+//
+
+#define LSAP_WELL_KNOWN_MAX_SUBAUTH_LEVEL ((ULONG) 0x00000003L)
+
+//
+// Constants relating to Sid's
+//
+
+#define LSAP_MAX_SUB_AUTH_COUNT (0x00000010L)
+#define LSAP_MAX_SIZE_TEXT_SUBA (0x00000009L)
+#define LSAP_MAX_SIZE_TEXT_SID_HDR (0x00000020L)
+#define LSAP_MAX_SIZE_TEXT_SID \
+ (LSAP_MAX_SIZE_TEXT_SID_HDR + \
+ (LSAP_MAX_SUB_AUTH_COUNT * LSAP_MAX_SIZE_TEXT_SUBA))
+
+
+//
+// Well Known Sid Table Entry
+//
+
+typedef struct _LSAP_WELL_KNOWN_SID_ENTRY {
+
+ PSID Sid;
+ SID_NAME_USE Use;
+ UNICODE_STRING Name;
+ UNICODE_STRING DomainName;
+
+} LSAP_WELL_KNOWN_SID_ENTRY, *PLSAP_WELL_KNOWN_SID_ENTRY;
+
+//
+// Well Known Sid Table Pointer
+//
+
+PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSids;
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Lookup Sids and Names - Private Function Definitions //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+LsaIInitializeWellKnownSids(
+ OUT PLSAP_WELL_KNOWN_SID_ENTRY *WellKnownSids
+ );
+
+BOOLEAN
+LsaIInitializeWellKnownSid(
+ OUT PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSids,
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex,
+ IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
+ IN UCHAR SubAuthorityCount,
+ IN OPTIONAL PULONG SubAuthorities,
+ IN PWSTR Name,
+ IN PWSTR Description,
+ IN SID_NAME_USE Use
+ );
+
+BOOLEAN
+LsapDbLookupIndexWellKnownSid(
+ IN PLSAPR_SID Sid,
+ OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ );
+
+BOOLEAN
+LsapDbLookupIndexWellKnownSidName(
+ IN PLSAPR_UNICODE_STRING Name,
+ OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ );
+
+NTSTATUS
+LsapDbGetNameWellKnownSid(
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex,
+ OUT PLSAPR_UNICODE_STRING Name,
+ OUT OPTIONAL PLSAPR_UNICODE_STRING DomainName
+ );
+
+BOOLEAN
+LsapDbLookupIndexWellKnownName(
+ IN OPTIONAL PLSAPR_UNICODE_STRING Name,
+ OUT PLSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ );
+
+NTSTATUS
+LsapDbLookupIsolatedWellKnownSids(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupSidsInLocalDomains(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbLookupSidsInLocalDomain(
+ IN ULONG LocalDomain,
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupSidsInPrimaryDomain(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupSidsInTrustedDomains(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupTranslateUnknownSids(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN ULONG MappedCount
+ );
+
+NTSTATUS
+LsapDbLookupTranslateUnknownSidsInDomain(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupIsolatedNames(
+ IN ULONG Count,
+ IN ULONG IsolatedNameCount,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ );
+
+NTSTATUS
+LsapDbLookupIsolatedWellKnownNames(
+ IN ULONG Count,
+ IN ULONG IsolatedNameCount,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ );
+
+NTSTATUS
+LsapDbLookupIsolatedDomainNames(
+ IN ULONG Count,
+ IN ULONG IsolatedNameCount,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ );
+
+NTSTATUS
+LsapDbLookupIsolatedDomainName(
+ IN ULONG NameIndex,
+ IN PLSAPR_UNICODE_STRING IsolatedName,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN OUT PULONG MappedIsolatedNameCount
+ );
+
+NTSTATUS
+LsapDbLookupNamesInLocalDomains(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbLookupNamesInLocalDomain(
+ IN ULONG LocalDomain,
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupNamesInPrimaryDomain(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupNamesInTrustedDomains(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupTranslateNameDomain(
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OPTIONAL PLSA_TRANSLATED_SID TranslatedSid,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ OUT PLONG DomainIndex
+ );
+
+NTSTATUS
+LsapDbLookupTranslateUnknownNames(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN ULONG MappedCount
+ );
+
+NTSTATUS
+LsapDbLookupTranslateUnknownNamesInDomain(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+NTSTATUS
+LsapDbLookupDispatchWorkerThreads(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapRtlValidateControllerTrustedDomain(
+ IN PLSAPR_UNICODE_STRING DomainControllerName,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE PolicyHandle
+ );
+
+NTSTATUS
+LsapDbLookupCreateListReferencedDomains(
+ OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ IN ULONG InitialMaxEntries
+ );
+
+NTSTATUS
+LsapDbLookupAddListReferencedDomains(
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ OUT PLONG DomainIndex
+ );
+
+BOOLEAN
+LsapDbLookupListReferencedDomains(
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN PLSAPR_SID DomainSid,
+ OUT PLONG DomainIndex
+ );
+
+NTSTATUS
+LsapDbLookupGrowListReferencedDomains(
+ IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN ULONG MaximumEntries
+ );
+
+NTSTATUS
+LsapDbLookupMergeDisjointReferencedDomains(
+ IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST FirstReferencedDomainList,
+ IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST SecondReferencedDomainList,
+ OUT PLSAPR_REFERENCED_DOMAIN_LIST *OutputReferencedDomainList,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbLookupInitialize(
+ );
+
+NTSTATUS
+LsapDbLookupInitializeWorkQueue(
+ );
+
+NTSTATUS
+LsapDbLookupInitializeWorkList(
+ OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapDbLookupInitializeWorkItem(
+ OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
+ );
+
+NTSTATUS
+LsapDbLookupAcquireWorkQueueLock(
+ );
+
+VOID LsapDbLookupReleaseWorkQueueLock();
+
+NTSTATUS
+LsapDbLookupLocalDomains(
+ OUT PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
+ OUT PLSAPR_TRUST_INFORMATION AccountDomainTrustInformation,
+ OUT PLSAPR_TRUST_INFORMATION PrimaryDomainTrustInformation
+ );
+
+NTSTATUS
+LsapDbLookupNamesBuildWorkList(
+ IN ULONG Count,
+ IN PLSAPR_UNICODE_STRING Names,
+ IN PLSAPR_UNICODE_STRING PrefixNames,
+ IN PLSAPR_UNICODE_STRING SuffixNames,
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
+ );
+
+NTSTATUS
+LsapDbLookupSidsBuildWorkList(
+ IN ULONG Count,
+ IN PLSAPR_SID *Sids,
+ IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
+ IN PLSAPR_TRANSLATED_NAMES TranslatedNames,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount,
+ IN OUT PULONG CompletelyUnmappedCount,
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
+ );
+
+NTSTATUS
+LsapDbLookupCreateWorkList(
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
+ );
+
+NTSTATUS
+LsapDbLookupInsertWorkList(
+ IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapDbLookupDeleteWorkList(
+ IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapDbLookupSignalCompletionWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapDbLookupAwaitCompletionWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapDbAddWorkItemToWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
+ );
+
+NTSTATUS
+LsapDbLookupStopProcessingWorkList(
+ IN PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN NTSTATUS TerminationStatus
+ );
+
+VOID
+LsapDbUpdateMappedCountsWorkList(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapDbLookupNamesUpdateTranslatedSids(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
+ IN PLSA_TRANSLATED_SID TranslatedSids
+ );
+
+NTSTATUS
+LsapDbLookupSidsUpdateTranslatedNames(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
+ IN PLSA_TRANSLATED_NAME TranslatedNames
+ );
+
+VOID
+LsapDbLookupWorkerThreadStart(
+ );
+
+VOID
+LsapDbLookupWorkerThread(
+ IN BOOLEAN PrimaryThread
+ );
+
+NTSTATUS
+LsapDbLookupObtainWorkItem(
+ OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList,
+ OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
+ );
+
+NTSTATUS
+LsapDbLookupProcessWorkItem(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
+ );
+
+NTSTATUS
+LsapDbLookupCreateWorkItem(
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN LONG DomainIndex,
+ IN ULONG MaximumEntryCount,
+ OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
+ );
+
+NTSTATUS
+LsapDbLookupAddIndicesToWorkItem(
+ IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
+ IN ULONG Count,
+ IN PULONG Indices
+ );
+
+NTSTATUS
+LsapDbLookupComputeAdvisoryChildThreadCount(
+ IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
+ );
+
+NTSTATUS
+LsapDbLookupUpdateAssignableWorkItem(
+ IN BOOLEAN MoveToNextWorkList
+ );
+
+
+NTSTATUS
+LsapRtlExtractDomainSid(
+ IN PSID Sid,
+ OUT PSID *DomainSid
+ );
+
+VOID LsapDbLookupReturnThreadToPool();
+
+
+/*++
+
+PSID
+LsapDbWellKnownSid(
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ )
+
+Routine Description:
+
+ This macro function returns the Well Known Sid corresponding
+ to an index into the Well Known Sid table.
+
+Arguments:
+
+ WellKnownSidIndex - Index into the Well Known Sid information table.
+ It is the caller's responsibility to ensure that the given index
+ is valid.
+
+Return Value:
+
+--*/
+
+#define LsapDbWellKnownSid( WellKnownSidIndex ) \
+ (WellKnownSids[ WellKnownSidIndex ].Sid)
+
+PUNICODE_STRING
+LsapDbWellKnownSidName(
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ );
+
+
+/*++
+
+SID_NAME_USE
+LsapDbWellKnownSidNameUse(
+ IN LSAP_DB_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ )
+
+
+Routine Description:
+
+ This macro function returns the Sid Name Use of a Well Known Sid.
+
+Arguments:
+
+ WellKnownSidIndex - Index into the Well Known Sid information table.
+ It is the caller's responsibility to ensure that the given index
+ is valid.
+
+Return Value:
+
+--*/
+
+#define LsapDbWellKnownSidNameUse( WellKnownSidIndex ) \
+ (WellKnownSids[ WellKnownSidIndex ].Use)
+
+
+VOID
+LsapDbUpdateCountCompUnmappedNames(
+ OUT PLSAPR_TRANSLATED_SIDS TranslatedSids,
+ IN OUT PULONG CompletelyUnmappedCount
+ );
+
+/*++
+
+PUNICODE_STRING
+LsapDbWellKnownSidDescription(
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ )
+
+Routine Description:
+
+ This macro function returns the Unicode Description of a Well Known Sid.
+
+Arguments:
+
+ WellKnownSidIndex - Index into the Well Known Sid information table.
+ It is the caller's responsibility to ensure that the given index
+ is valid.
+
+Return Value:
+
+--*/
+
+#define LsapDbWellKnownSidDescription( WellKnownSidIndex ) \
+ (&(WellKnownSids[ WellKnownSidIndex ].DomainName))
+
+
+PUNICODE_STRING
+LsapDbWellKnownSidName(
+ IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex
+ );
+
+#define LsapDbAccessedBySidObject( ObjectTypeId ) \
+ (LsapDbState.DbObjectTypes[ ObjectTypeId ].AccessedBySid)
+
+#define LsapDbAccessedByNameObject( ObjectTypeId ) \
+ (LsapDbState.DbObjectTypes[ ObjectTypeId ].AccessedByName)
+
+#define LsapDbCompletelyUnmappedName(TranslatedName) \
+ (((TranslatedName)->DomainIndex == LSA_UNKNOWN_INDEX) && \
+ ((TranslatedName)->Use == SidTypeUnknown))
+
+#define LsapDbCompletelyUnmappedSid(TranslatedSid) \
+ (((TranslatedSid)->DomainIndex == LSA_UNKNOWN_INDEX) && \
+ ((TranslatedSid)->Use == SidTypeUnknown))
+
diff --git a/private/lsa/server/dbmisc.c b/private/lsa/server/dbmisc.c
new file mode 100644
index 000000000..603e2c6c3
--- /dev/null
+++ b/private/lsa/server/dbmisc.c
@@ -0,0 +1,545 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbmisc.c
+
+Abstract:
+
+ Local Security Authority - Miscellaneous API
+
+ This file contains worker routines for miscellaneous API that are
+ not specific to objects of a given type.
+
+Author:
+
+ Scott Birrell (ScottBi) January 15, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+NTSTATUS
+LsarClose(
+ IN OUT LSAPR_HANDLE *ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the LsaClose
+ API.
+
+ The LsaClose API closes a handle to an open object within the database.
+ If closing a handle to the Policy object and there are no objects still
+ open within the current connection to the LSA, the connection is closed.
+ If a handle to an object within the database is closed and the object is
+ marked for DELETE access, the object will be deleted when the last handle
+ to that object is closed.
+
+Arguments:
+
+ ObjectHandle - Handle returned from an LsaOpen<object type> or
+ LsaCreate<object type> call.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Acquire the Lsa Database Lock
+ //
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+ }
+
+ //
+ // Validate and close the object handle, dereference its container (if any).
+ //
+
+ if (*ObjectHandle == LsapPolicyHandle)
+ {
+#ifdef TRACK_HANDLE_CLOSE
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+#endif // TRACK_HANDLE_CLOSE
+ Status = STATUS_INVALID_HANDLE;
+ } else {
+ Status = LsapDbCloseObject(
+ ObjectHandle,
+ LSAP_DB_VALIDATE_HANDLE |
+ LSAP_DB_DEREFERENCE_CONTR |
+ LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES
+ );
+
+ }
+
+ LsapDbReleaseLock();
+ return Status;
+}
+
+
+NTSTATUS
+LsarDeleteObject(
+ IN OUT LSAPR_HANDLE *ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the LsaDelete
+ API.
+
+ The LsaDelete API deletes an object from the LSA Database. The object must be
+ open for DELETE access.
+
+Arguments:
+
+ ObjectHandle - Pointer to Handle from an LsaOpen<object type> or
+ LsaCreate<object type> call. On return, this location will contain
+ NULL if the call is successful.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - There is no object in the
+ target system's LSA Database having the name and type specified
+ by the handle.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS SavedStatus, IgnoreStatus;
+ LSAP_DB_HANDLE InternalHandle = *ObjectHandle;
+ BOOLEAN ObjectReferenced = FALSE;
+ ULONG ReferenceOptions = LSAP_DB_START_TRANSACTION;
+ ULONG DereferenceOptions = LSAP_DB_FINISH_TRANSACTION |
+ LSAP_DB_DEREFERENCE_CONTR;
+ PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
+ LSAPR_TRUST_INFORMATION OutputTrustInformation;
+ BOOLEAN TrustInformationPresent = FALSE;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
+
+ ObjectTypeId = InternalHandle->ObjectTypeId;
+
+ //
+ // Acquire the Lsa Database Lock
+ //
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+ }
+
+ //
+ // Verify that the Object handle is valid, is of the expected type and
+ // has all of the desired accesses granted. Reference the handle and
+ // open a database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ *ObjectHandle,
+ DELETE,
+ NullObject,
+ ReferenceOptions
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Perform object type specific pre-processing. Note that some
+ // pre-processing is also done within LsapDbReferenceObject(), for
+ // example, for local secrets.
+ //
+
+ switch (ObjectTypeId) {
+
+ case PolicyObject:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ case TrustedDomainObject:
+
+ if (LsapAdtAuditingPolicyChanges()) {
+
+ //
+ // If we're auditing deletions of TrustedDomain objects, we need
+ // to retrieve the TrustedDomain name and keep it for later when
+ // we generate the audit.
+ //
+
+ Status = LsapDbLookupSidTrustedDomainList(
+ NULL,
+ InternalHandle->Sid,
+ &TrustInformation
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ Status = LsapRpcCopyTrustInformation(
+ NULL,
+ &OutputTrustInformation,
+ TrustInformation
+ );
+
+ TrustInformationPresent = NT_SUCCESS( Status );
+ }
+ }
+
+ break;
+
+ case AccountObject:
+
+ {
+ PLSAPR_PRIVILEGE_SET Privileges;
+ LSAPR_HANDLE AccountHandle;
+ PLSAPR_SID AccountSid = NULL;
+ ULONG AuditEventId;
+
+ AccountHandle = *ObjectHandle;
+
+ AccountSid = LsapDbSidFromHandle( AccountHandle );
+
+ if (LsapAdtAuditingPolicyChanges()) {
+
+ Status = LsarEnumeratePrivilegesAccount(
+ AccountHandle,
+ &Privileges
+ );
+
+ if (!NT_SUCCESS( Status )) {
+ DbgPrint("LsarEnumeratePrivilegesAccount ret'd %x\n",Status);
+ break;
+ }
+
+
+ AuditEventId = SE_AUDITID_USER_RIGHT_REMOVED;
+
+ //
+ // Audit the privilege set change. Ignore failures from Auditing.
+ //
+
+ IgnoreStatus = LsapAdtGenerateLsaAuditEvent(
+ AccountHandle,
+ SE_CATEGID_POLICY_CHANGE,
+ AuditEventId,
+ (PPRIVILEGE_SET)Privileges,
+ 1,
+ (PSID *) &AccountSid,
+ 0,
+ NULL,
+ NULL
+ );
+
+ MIDL_user_free( Privileges );
+ }
+ }
+
+ break;
+
+ case SecretObject:
+
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+ Status = LsapDbDeleteObject( *ObjectHandle );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+ //
+ // Decrement the Reference Count so that the object's handle will be
+ // freed upon dereference.
+ //
+
+ InternalHandle->ReferenceCount--;
+
+ //
+ // Perform object post-processing. The only post-processing is
+ // the auditing of TrustedDomain object deletion.
+ //
+
+ if (LsapAdtAuditingPolicyChanges() && TrustInformationPresent) {
+
+ if (ObjectTypeId == TrustedDomainObject) {
+
+ SavedStatus = Status;
+
+ //
+ // Note that the object handle cannot be passed since it has gone
+ // away.
+ //
+
+ Status = LsapAdtGenerateLsaAuditEvent(
+ NULL,
+ SE_CATEGID_POLICY_CHANGE,
+ SE_AUDITID_TRUSTED_DOMAIN_REM,
+ NULL,
+ 1,
+ &InternalHandle->Sid,
+ 1,
+ (PUNICODE_STRING) &OutputTrustInformation.Name,
+ NULL
+ );
+
+ //
+ // Ignore failure status from auditing.
+ //
+
+ Status = SavedStatus;
+
+ //
+ // Call fgs routine because we want to free the graph of the
+ // structure, but not the top level of the structure.
+ //
+
+ _fgs__LSAPR_TRUST_INFORMATION ( &OutputTrustInformation );
+ TrustInformation = NULL;
+ }
+ }
+
+ //
+ // Delete new object from the in-memory cache (if any)
+ //
+
+ if (LsapDbIsCacheSupported( ObjectTypeId)) {
+
+ if (LsapDbIsCacheValid( ObjectTypeId)) {
+
+ switch (ObjectTypeId) {
+
+ case AccountObject:
+
+ IgnoreStatus = LsapDbDeleteAccount( InternalHandle->Sid );
+ break;
+
+ default:
+
+ break;
+ }
+ }
+ }
+
+ //
+ // Audit the deletion
+ //
+
+ IgnoreStatus = NtDeleteObjectAuditAlarm(
+ &LsapState.SubsystemName,
+ *ObjectHandle,
+ InternalHandle->GenerateOnClose
+ );
+
+DeleteObjectFinish:
+
+ //
+ // If we referenced the object, dereference it, close the database
+ // transaction, notify the replicator of the delete, release the LSA
+ // Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ ObjectHandle,
+ InternalHandle->ObjectTypeId,
+ DereferenceOptions,
+ SecurityDbDelete,
+ Status
+ );
+
+ ObjectReferenced = FALSE;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+ }
+
+ //
+ // Release the Lsa database lock.
+ //
+
+ LsapDbReleaseLock();
+
+ return(Status);
+
+DeleteObjectError:
+
+ goto DeleteObjectFinish;
+}
+
+
+NTSTATUS
+LsarDelete(
+ IN LSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the former LSA server RPC worker routine for the
+ LsaDelete API. It has been termorarily retained for compatibility
+ with pre Beta 2 versions 1.369 and earlier of the system. It has been
+ necessary to replace this routine with a new one, LsarDeleteObject(),
+ on the RPC interface. This is because, like LsarClose(), a pointer to a
+ handle is required rather than a handle so that LsarDeleteObject() can
+ inform the RPC server calling stub that the handle has been deleted by
+ returning NULL. The client wrapper for LsaDelete() will try to call
+ LsarDeleteObject(). If the server code does not contain this interface,
+ the client will call LsarDelete(). In this event, the server's
+ LSAPR_HANDLE_rundown() routine may attempt to rundown the handle after it
+ has been deleted (versions 1.363 - 369 only).
+
+ The LsaDelete API deletes an object from the LSA Database. The object must be
+ open for DELETE access.
+
+Arguments:
+
+ ObjectHandle - Handle from an LsaOpen<object type> or LsaCreate<object type>
+ call.
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - There is no object in the
+ target system's LSA Database having the name and type specified
+ by the handle.
+--*/
+
+{
+ //
+ // Call the replacement routine LsarDeleteObject()
+ //
+
+ return( LsarDeleteObject((LSAPR_HANDLE *) &ObjectHandle));
+}
+
+
+NTSTATUS
+LsarChangePassword(
+ IN PLSAPR_UNICODE_STRING ServerName,
+ IN PLSAPR_UNICODE_STRING DomainName,
+ IN PLSAPR_UNICODE_STRING AccountName,
+ IN PLSAPR_UNICODE_STRING OldPassword,
+ IN PLSAPR_UNICODE_STRING NewPassword
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaChangePassword API is used to change a user account's password.
+ The user must have appropriate access to the user account and must
+ know the current password value.
+
+
+Arguments:
+
+ ServerName - The name of the Domain Controller at which the password
+ can be changed.
+
+ DomainName - The name of the domain in which the account exists.
+
+ AccountName - The name of the account whose password is to be changed.
+
+ NewPassword - The new password value.
+
+ OldPassword - The old (current) password value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, e.g.
+ contains characters that can't be entered from the keyboard.
+
+ STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
+ from being changed. This may be for an number of reasons,
+ including time restrictions on how often a password may be changed
+ or length restrictions on the provided (new) 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.
+
+ STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
+ current password.
+
+ STATUS_NO_SUCH_USER - The SID provided does not lead to a user
+ account.
+
+ STATUS_CANT_UPDATE_MASTER - An attempt to update the master copy
+ of the password was unsuccessful. Please try again later.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ DBG_UNREFERENCED_PARAMETER( ServerName );
+ DBG_UNREFERENCED_PARAMETER( DomainName );
+ DBG_UNREFERENCED_PARAMETER( AccountName );
+ DBG_UNREFERENCED_PARAMETER( OldPassword );
+ DBG_UNREFERENCED_PARAMETER( NewPassword );
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ return(Status);
+}
+
+
+
diff --git a/private/lsa/server/dbobject.c b/private/lsa/server/dbobject.c
new file mode 100644
index 000000000..0a8aa7f7b
--- /dev/null
+++ b/private/lsa/server/dbobject.c
@@ -0,0 +1,3965 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbobject.c
+
+Abstract:
+
+ Local Security Authority - LSA Database Public Object Management Routines
+
+ This module contains the public routines that perform LSA Database object
+ manipulation. These routines are exported to the rest of the
+ LSA, function prototypes of these routines will be found in db.h. These
+ exported routines present an implementation-independent hierarchic
+ object-based view of the LSA Database and are used exclusively by the
+ LSA API. See the Additional Notes further below for a description of
+ the LSA Database model.
+
+ Routines in this module that are private to the object management
+ function have function prototypes in dbp.h.
+
+Author:
+
+ Scott Birrell (ScottBi) August 26, 1991
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+Notes on the LSA Database Architecture
+
+OBJECT STRUCTURE
+
+ The LSA Database is an hierarchic structure containing "objects" of
+ several "types". Objects have either a "name" or a Sid depending only
+ on object type, and may have data stored with them under named
+ "attributes". The database hierarchy contains a single root object
+ called the Lsa Database object and having name "Policy". This object
+ represents the entire LSA Database. Currently, the Lsa Database has a
+ simple hierarchy consisting of only two levels.
+
+ Policy
+
+ Account Objects, Trusted Domain Objects, Secret Objects
+
+ The Policy object is called a "Container Object" for the other
+ object types. The attributes of the Policy object house information
+ that applies generally to the whole database. The single Policy object
+ has name "Policy".
+
+ Account objects represent those user accounts which are treated specially
+ on the local system, but not necessarily so on other systems. Such
+ accounts may have additional privileges, or system quotas for example.
+ Account objects are referenced by Sid.
+
+ TrustedDomain objects describe domains which the system has a trust
+ relationship with. These objects are referenced by Sid.
+
+ Secret Objects are named entities containing information that is protected
+ in some way. Secret objects are referenced by name.
+
+OBJECT ACCESS AND DATABASE SECURITY
+
+ Each object in the LSA Database is protected by a Security Descriptor which
+ contains a Discretionary Access Control List (DACL) defining which groups
+ can access the object and in which ways. Before an object can be
+ accessed, it must first be "opened" with the desired accesses requested
+ that are needed to perform the desired operations on the object. Opening
+ an object returns a "handle" to the object. This handle may then be
+ specified on Lsa services that access the object. After use, the handle
+ to the object should then be "closed". Closing the handle renders it
+ invalid.
+
+CONCURRENCY OF ACCESS
+
+ More than one handle may be open to an object concurrently, possibly with
+ different accesses granted.
+
+PERMANENCY OF OBJECTS
+
+ All LSA Database objects are backed by non-volatile storage media, that is,
+ they remain in existence until deleted via the LsaDelete() service.
+ The Policy object cannot be deleted and the single object of this type cannot
+ be created via the public LSA service interface.
+
+ Objects will not be deleted while there are open handles to them.
+ When access to an object is no longer required, the handle should be
+ "closed".
+
+DATABASE DESIGN
+
+ The LSA Database is of an hierarchic design permitting future extension.
+ Currently the database has the following simple hierarchy:
+
+ Policy Object (name = Policy)
+
+ Account Objects TrustedDomain Objects Secret Objects
+
+ The single object of type Policy is at the topmost level and serves as
+ a parent or "container" object for objects of the other three types.
+ Since named objects of different types may potentially reside in the
+ same container object in the future, an object is referenced uniquely
+ only if the object name and type together with the identity of its
+ container object (currently always the Policy object) are known.
+ To implement this kind of reference easily, objects of the same type
+ are held within a "classifying directory" which has a name derived
+ from the object's type as follows:
+
+ Object Type Containing Directory Name
+
+ Policy Not required
+ Account Accounts
+ TrustedDomain Domains
+ Secret Secrets
+
+IMPLEMENTATION NOTES
+
+ The LSA Database is currently implemented as a subtree of the Configuration
+ Registry. This subtree has the following form
+
+ \Policy\Accounts\<account_object_Rid>\<account_object_attribute_name>
+ \Domains\<trusted_domain_Rid>\<trus_domain_object_attribute_name>
+ \Secrets\<secret_name>\<secret_object_attribute_name>
+ \<policy_object_attribute_name>
+
+ where each item between \..\ is the name of a Registry Key and
+ "Rid" is a character name made out of the Relative Id (lowest
+ subauthority extracted from the object's Sid). Named object attributes
+ can have binary data "values".
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+#include "adtp.h"
+
+
+NTSTATUS
+LsapDbOpenObject(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG Options,
+ OUT PLSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens an existing object in the LSA Database. An error
+ is returned if the object does not already exist. The LSA Database must
+ be already locked when calling this function and any container handle
+ must have been validated as having the necessary access for creation
+ of an object of the given type.
+
+Arguments:
+
+ ObjectInformation - Pointer to information describing this object. The
+ following information items must be specified:
+
+ o Object Type Id
+ o Object Logical Name (as ObjectAttributes->ObjectName, a pointer to
+ a Unicode string)
+ o Container object handle (for any object except the root Policy object).
+ o Object Sid (if any)
+
+ All other fields in ObjectAttributes portion of ObjectInformation
+ such as SecurityDescriptor are ignored.
+
+ DesiredAccess - Specifies the Desired accesses to the Lsa object
+
+ Options - Specifies optional additional actions to be taken:
+
+ LSAP_DB_TRUSTED - A trusted handle is wanted regardless of the trust
+ status of any container handle provided in ObjectInformation.
+
+ LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK - Omit the check for a BDC.
+ This flag is set usually because an object (e.g. a local secret)
+ is local to a specific computer and is not replicated. Objects
+ of this type may be created, updated or deleted by non-trusted
+ clients on BDC's, so no BDC check is required.
+
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit replicator notification
+ on object updates. This flag will be stored in the handle
+ created for the object and retrieved when committing an update
+ to the object via LsapDbDereferenceObject().
+
+ ObjectHandle - Receives the handle to the object.
+
+Return Value:
+
+ NTSTATUS - Standard NT status code
+
+ STATUS_INVALID_PARAMETER - One or more parameters invalid.
+ - Invalid syntax of parameters, e.g Sid
+ - Sid not specified when required for object type
+ - Name specified when not allowed.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the request (e.g. memory for reading object's
+ Security Descriptor).
+
+ STATUS_OBJECT_NOT_FOUND - Object does not exist.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG SecurityDescriptorLength;
+ LSAP_DB_HANDLE NewObjectHandle = NULL;
+ PSECURITY_DESCRIPTOR ContainerSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ OBJECT_ATTRIBUTES OpenKeyObjectAttributes;
+ ULONG States = Options & LSAP_DB_STATE_MASK;
+ ULONG ResetStates = 0;
+ LSAPR_HANDLE OutputHandle = NULL;
+ LSAP_DB_HANDLE InternalOutputHandle = NULL;
+ LSAP_DB_HANDLE ContainerHandle = NULL;
+
+ PSECURITY_DESCRIPTOR SavedSecurityDescriptor =
+ ObjectInformation->ObjectAttributes.SecurityDescriptor;
+
+ //
+ // Validate the Object Information parameter.
+ //
+
+ Status = LsapDbVerifyInformationObject( ObjectInformation );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenObjectError;
+ }
+
+ //
+ // Verify that the Lsa database is now locked.
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Allocate and initialize a handle for the object. The object's
+ // Registry Key, Logical and Physical Names will be derived from
+ // the given ObjectInformation and pointers to them will be stored in
+ // the handle.
+ //
+
+ OutputHandle = LsapDbCreateHandle( ObjectInformation, Options );
+ InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (OutputHandle == NULL) {
+
+ goto OpenObjectError;
+ }
+
+ //
+ // Setup Object Attributes structure for opening the Registry key of
+ // the object. Specify as path the Physical Name of the object, this
+ // being the path of the object's Registry Key relative to the
+ // LSA Database root key.
+ //
+
+ InitializeObjectAttributes(
+ &OpenKeyObjectAttributes,
+ &InternalOutputHandle->PhysicalNameU,
+ OBJ_CASE_INSENSITIVE,
+ LsapDbState.DbRootRegKeyHandle,
+ NULL
+ );
+
+ //
+ // Now attempt to open the object's Registry Key. Store the Registry
+ // Key handle in the object's handle.
+ //
+
+ Status = RtlpNtOpenKey(
+ (PHANDLE) &InternalOutputHandle->KeyHandle,
+ KEY_READ | KEY_WRITE,
+ &OpenKeyObjectAttributes,
+ 0L
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ InternalOutputHandle->KeyHandle = NULL; // For cleanup purposes
+ goto OpenObjectError;
+ }
+
+ //
+ // The object exists. Unless access checking is to be bypassed, we
+ // need to access the object's Security Descriptor and perform an
+ // access check. The Security Descriptor is stored as the object's
+ // SecDesc attribute, so we need to read this. First, we must query the
+ // size of the Security Descriptor to determine how much memory to
+ // allocate for reading it. The query is done by issuing a read of the
+ // object's SecDesc subkey with a NULL output buffer and zero size
+ // specified.
+ //
+
+ if (!(InternalOutputHandle->Trusted)) {
+
+ SecurityDescriptorLength = 0;
+
+ Status = LsapDbReadAttributeObject(
+ OutputHandle,
+ &LsapDbNames[SecDesc],
+ NULL,
+ &SecurityDescriptorLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenObjectError;
+ }
+
+ //
+ // Allocate a buffer from the Lsa Heap for the existing object's SD.
+ //
+
+ SecurityDescriptor = LsapAllocateLsaHeap( SecurityDescriptorLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (SecurityDescriptor == NULL) {
+
+ goto OpenObjectError;
+ }
+
+ //
+ // Read the SD. It is the value of the SecDesc subkey.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ OutputHandle,
+ &LsapDbNames[SecDesc],
+ SecurityDescriptor,
+ &SecurityDescriptorLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenObjectError;
+ }
+
+ //
+ // Reference the SD read from the LSA Database from the object
+ // information.
+ //
+
+ ObjectInformation->ObjectAttributes.SecurityDescriptor =
+ SecurityDescriptor;
+
+ //
+ // Request the desired accesses and store them in the object's handle.
+ // granted.
+ //
+
+ Status = LsapDbRequestAccessObject(
+ OutputHandle,
+ ObjectInformation,
+ DesiredAccess,
+ Options
+ );
+
+ //
+ // If the accesses are granted, the open has completed successfully.
+ // Store the container object handle in the object's handle and
+ // return the handle to the caller..
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenObjectError;
+ }
+ }
+
+ *ObjectHandle = OutputHandle;
+
+OpenObjectFinish:
+
+ //
+ // Restore the saved Security Descriptor reference in the object
+ // information.
+ //
+
+ ObjectInformation->ObjectAttributes.SecurityDescriptor =
+ SavedSecurityDescriptor;
+
+ //
+ // If necessary, free the memory allocated for the Security Descriptor
+ //
+
+ if (SecurityDescriptor != NULL) {
+
+ LsapFreeLsaHeap( SecurityDescriptor );
+ }
+
+ return(Status);
+
+OpenObjectError:
+
+ //
+ // If necessary, free the handle we created.
+ //
+
+ if (OutputHandle != NULL) {
+
+ LsapDbFreeHandle(OutputHandle);
+ }
+
+ goto OpenObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbCreateObject(
+ IN OUT PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG CreateDisposition,
+ IN ULONG Options,
+ IN OPTIONAL PLSAP_DB_ATTRIBUTE Attributes,
+ IN ULONG TypeSpecificAttributeCount,
+ OUT PLSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates an object in the LSA Database, together with
+ the set of attributes, such as Security Descriptor that are common
+ to all object types. The object will be left in the open state
+ and the caller may use the returned handle to create the type-
+ specific attributes.
+
+ NOTE: For an object creation, it is the responsibility of the calling
+ LSA object creation routine to verify that the necessary access to the
+ container object is granted. That access is dependent on the type of
+ LSA object being created.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called. No Lsa Database transaction may be pending when
+ this function is called.
+
+Arguments:
+
+ ObjectInformation - Pointer to information describing this object. The
+ following information items must be specified:
+
+ o Object Type Id
+ o Object Logical Name (as ObjectAttributes->ObjectName, a pointer to
+ a Unicode string)
+ o Container object handle (for any object except the root Policy object).
+ o Object Sid (if any)
+
+ All other fields in ObjectAttributes portion of ObjectInformation
+ such as SecurityDescriptor are ignored.
+
+ DesiredAccess - Specifies the Desired accesses to the object.
+
+ CreateDisposition - Specifies the Creation Disposition. This is the
+ action to take depending on whether the object already exists.
+
+ LSA_OBJECT_CREATE - Create the object if it does not exist. If
+ the object already exists, return an error.
+
+ LSA_OBJECT_OPEN_IF - Create the object if it does not exist. If
+ the object already exists, just open it.
+
+ Options - Specifies optional information and actions to be taken
+
+ LSAP_DB_ACQUIRE_LOCK - Acquire the LSA Database lock
+
+ LSAP_DB_TRUSTED - A Trusted Handle is wanted regardless of the
+ Trust status of any container handle provided in
+ ObjectInformation.
+
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification of the
+ object creation to Replicator.
+
+ Note, this routine performs a complete database transaction so
+ there is no option to start one.
+
+ Attributes - Optional pointer to an array of attribute
+ names and values. These are specific to the type of object.
+
+ TypeSpecificAttributeCount - Number of elements in the array
+ referenced by the Attributes parameter.
+
+ ObjectHandle - Receives the handle to the object.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_PARAMETER - The given Sid is invalid.
+
+ STATUS_OBJECT_NAME_EXISTS - An object having the given Sid
+ already exists and has been opened because LSA_OBJECT_OPEN_IF
+ disposition has been specified. This is a warning only.
+
+ STATUS_OBJECT_NAME_COLLISION - An object having the given Sid
+ already exists but has not been opened because LSA_OBJECT_CREATE
+ disposition has been specified. This is an error.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus, IgnoreStatus;
+ OBJECT_ATTRIBUTES OpenKeyObjectAttributes;
+ ULONG CloseOptions;
+ BOOLEAN AcquiredLock = FALSE;
+ BOOLEAN CreatedObject = FALSE;
+ BOOLEAN OpenedObject = FALSE;
+ BOOLEAN OpenedTransaction = FALSE;
+ LSAPR_HANDLE OutputHandle = NULL;
+ LSAP_DB_HANDLE InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
+ LSAP_DB_HANDLE ContainerHandle = NULL;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
+
+ //
+ // Verify the creation disposition.
+ //
+
+ if ((CreateDisposition != LSAP_DB_OBJECT_CREATE) &&
+ (CreateDisposition != LSAP_DB_OBJECT_OPEN_IF)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto CreateObjectError;
+ }
+
+ //
+ // Optionally lock the Lsa Database
+ //
+
+ if (Options & LSAP_DB_ACQUIRE_LOCK) {
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+ }
+
+ AcquiredLock = TRUE;
+
+ //
+ // Try to open the object. It is permissible for the object to
+ // exist already if LSA_OBJECT_OPEN_IF disposition was specified.
+ //
+
+ Status = LsapDbOpenObject(
+ ObjectInformation,
+ DesiredAccess,
+ Options,
+ &OutputHandle
+ );
+
+ InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // The object was successfully opened. If LSA_OBJECT_OPEN_IF
+ // disposition was specified, we're done, otherwise, we
+ // return a collision error.
+ //
+
+ OpenedObject = TRUE;
+
+ Status = STATUS_OBJECT_NAME_EXISTS;
+
+ if (CreateDisposition == LSAP_DB_OBJECT_OPEN_IF) {
+
+ goto CreateObjectFinish;
+ }
+
+ Status = STATUS_OBJECT_NAME_COLLISION;
+
+ if (CreateDisposition == LSAP_DB_OBJECT_CREATE) {
+
+ goto CreateObjectError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // The object was not successfully opened. If this is for any
+ // reason other than that the object was not found, return an error.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto CreateObjectError;
+ }
+
+ //
+ // The object was not found. Prepare to create it. First, we need to
+ // check that any maximum limit on the number of objects of this type
+ // imposed will not be exceeded.
+ //
+
+ Status = LsapDbCheckCountObject(ObjectTypeId);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+
+ //
+ // Next we need to create a handle for the new object.
+ //
+
+ OutputHandle = LsapDbCreateHandle( ObjectInformation, Options );
+ InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (OutputHandle == NULL) {
+
+ goto CreateObjectError;
+ }
+
+ //
+ // Verify that the requested accesses can be given to the handle that
+ // has been opened and grant them if so.
+ //
+
+ Status = LsapDbRequestAccessNewObject(
+ OutputHandle,
+ ObjectInformation,
+ DesiredAccess,
+ Options
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+
+ //
+ // Open a Registry transaction for creation of the object.
+ //
+
+ Status = LsapDbOpenTransaction();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+
+ OpenedTransaction = TRUE;
+
+ //
+ // Add a registry transaction to create the Registry key for the new
+ // Database object.
+ //
+
+ Status = RtlAddActionToRXact(
+ LsapDbState.RXactContext,
+ RtlRXactOperationSetValue,
+ &InternalOutputHandle->PhysicalNameU,
+ ObjectTypeId,
+ NULL, // No Key Value needed
+ 0L
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+
+ //
+ // Create the Security Descriptor for the new object. This will be
+ // stored in Self-Relative form as the value of the SecDesc attribute
+ // of the new object.
+ //
+
+ Status = LsapDbCreateSDAttributeObject(
+ OutputHandle,
+ ObjectInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+
+ //
+ // The self-relative SD returned is not needed here or by callers of
+ // this routine.
+ //
+
+ if (ObjectInformation->ObjectAttributes.SecurityDescriptor != NULL) {
+
+ RtlFreeHeap(
+ RtlProcessHeap(),
+ 0,
+ ObjectInformation->ObjectAttributes.SecurityDescriptor
+ );
+
+ ObjectInformation->ObjectAttributes.SecurityDescriptor = NULL;
+ }
+
+ //
+ // Write the type-specific attributes (if any) for the object).
+ //
+
+ if (TypeSpecificAttributeCount != 0) {
+
+ Status = LsapDbWriteAttributesObject(
+ OutputHandle,
+ Attributes,
+ TypeSpecificAttributeCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+ }
+
+ //
+ // Apply the Registry Transaction to create the object. Note
+ // that we have to create the object before we can open its
+ // registry key for placement within the handle.
+ //
+
+ Status = LsapDbResetStates(
+ OutputHandle,
+ Options | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbNew,
+ Status
+ );
+
+ OpenedTransaction = FALSE;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateObjectError;
+ }
+
+ //
+ // Increment the count of objects created. It should not have
+ // changed since we're still holding the LSA Database lock.
+ // NOTE: Count is decremented on error inside LsapDbDeleteObject()
+ //
+
+ LsapDbIncrementCountObject(ObjectInformation->ObjectTypeId);
+
+ CreatedObject = TRUE;
+
+ //
+ // The object has now been created. We need to obtain its Registry
+ // Key handle so that we can save it in the Object Handle.
+ // Setup Object Attributes structure for opening the Registry key of
+ // the object. Specify as path the Physical Name of the object, this
+ // being the path of the object's Registry Key relative to the
+ // LSA Database root key.
+ //
+
+ InitializeObjectAttributes(
+ &OpenKeyObjectAttributes,
+ &InternalOutputHandle->PhysicalNameU,
+ OBJ_CASE_INSENSITIVE,
+ LsapDbState.DbRootRegKeyHandle,
+ NULL
+ );
+
+ //
+ // Now attempt to open the object's Registry Key. Store the Registry
+ // Key handle in the object's handle.
+ //
+
+ Status = RtlpNtOpenKey(
+ (PHANDLE) &InternalOutputHandle->KeyHandle,
+ KEY_READ | KEY_WRITE,
+ &OpenKeyObjectAttributes,
+ 0L
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ InternalOutputHandle->KeyHandle = NULL;
+ goto CreateObjectError;
+ }
+
+ //
+ // Add the new object to the in-memory cache (if any). This is done
+ // after all other actions, so that no removal from the cache is required
+ // on the error paths. If the object cannot be added to the cache, the
+ // cache routine automatically disables the cache.
+ //
+
+ if (LsapDbIsCacheSupported( ObjectTypeId)) {
+
+ if (LsapDbIsCacheValid( ObjectTypeId)) {
+
+ switch (ObjectTypeId) {
+
+ case AccountObject:
+
+ IgnoreStatus = LsapDbCreateAccount(
+ InternalOutputHandle->Sid,
+ NULL
+ );
+ break;
+
+ default:
+
+ break;
+ }
+ }
+ }
+
+CreateObjectFinish:
+
+ //
+ // Return NULL or a handle to the newly created and opened object.
+ //
+
+ *ObjectHandle = OutputHandle;
+ return(Status);
+
+CreateObjectError:
+
+ //
+ // Cleanup after error. Various variables are set non-null if
+ // there is cleanup work to do.
+ //
+
+ //
+ // If necessary, abort the Registry Transaction to create the object
+ //
+
+ if (OpenedTransaction) {
+
+ Status = LsapDbResetStates(
+ OutputHandle,
+ LSAP_DB_FINISH_TRANSACTION,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ //
+ // If we opened the object, close it.
+ //
+
+ if (OpenedObject) {
+
+ CloseOptions = 0;
+ SecondaryStatus = LsapDbCloseObject( &OutputHandle, CloseOptions );
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ LsapLogError(
+ "LsapDbCreateObject: LsapDbCloseObject failed 0x%lx\n",
+ SecondaryStatus
+ );
+ }
+
+ OutputHandle = NULL;
+ InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
+
+ } else if (CreatedObject) {
+
+ //
+ // If we created the object, convert its handle into a trusted
+ // handle and delete it.
+ //
+
+ InternalOutputHandle->Trusted = TRUE;
+
+ SecondaryStatus = LsarDelete( OutputHandle );
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ LsapLogError(
+ "LsapDbCreateObject: LsarDeleteObject failed 0x%lx\n",
+ SecondaryStatus
+ );
+ }
+
+ } else if (OutputHandle != NULL) {
+
+ //
+ // If we just created the handle, free it.
+ //
+
+ LsapDbFreeHandle( OutputHandle );
+
+ OutputHandle = NULL;
+ InternalOutputHandle = (LSAP_DB_HANDLE) OutputHandle;
+ }
+
+ goto CreateObjectFinish;
+
+ DBG_UNREFERENCED_PARAMETER( CloseOptions );
+}
+
+
+NTSTATUS
+LsapDbRequestAccessObject(
+ IN OUT LSAPR_HANDLE ObjectHandle,
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs an access check for an LSA Database object. While
+ impersonating an RPC client, the specified Desired Accesses are reconciled
+ with the Discretionary Access Control List (DACL) in the object's
+ Security Descriptor. Note that the object's Security Descriptor is
+ passed explicitly so that this routine can be called for new objects
+ for which a SD has been constructed but not yet written to the
+ Registry.
+
+Arguments:
+
+ ObjectHandle - Handle to object. The handle will receive the
+ granted accesses if the call is successful.
+
+ ObjectInformation - Pointer to object's information. As a minimum, the
+ object's Security Descriptor must be set up.
+
+ DesiredAccess - Specifies a mask of the access types desired to the
+ object.
+
+ Options - Specifies optional actions to be taken
+
+ LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK - Omit the check for a BDC for
+ a create/update/delete operation on a local (non-replicated)
+ object such as a local secret.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Not all of the Desired Accessed can be
+ granted to the caller.
+
+ STATUS_BACKUP_CONTROLLER - A create, update or delete operation
+ is not allowed for a non-trusted client for this object on a BDC,
+ because the object is global to all DC's for a domain and is replicated.
+
+ Errors from RPC client impersonation
+--*/
+
+{
+ NTSTATUS Status, RevertStatus, AccessStatus;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = InternalHandle->ObjectTypeId;
+ BOOLEAN WriteOperation = FALSE;
+ ULONG EffectiveOptions = Options | InternalHandle->Options;
+
+ //
+ // If the system is a Backup Domain Controller, disallow update
+ // operations for non-trusted callers except in special cases such
+ // as local non-replicated objects. In these special cases, the
+ // flag LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK will be already set
+ // in the handle Options.
+ //
+
+ WriteOperation = RtlAreAnyAccessesGranted(
+ LsapDbState.DbObjectTypes[InternalHandle->ObjectTypeId].WriteOperations,
+ DesiredAccess
+ );
+
+ if ((LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) &&
+ (!InternalHandle->Trusted) &&
+ WriteOperation &&
+ (!(EffectiveOptions & LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK))) {
+
+ Status = STATUS_BACKUP_CONTROLLER;
+ return Status;
+ }
+
+ //
+ // Common path for Object Open and Creation. We need to reconcile
+ // the desired accesses to the object with the Discretionary Access
+ // Control List contained in the Security Descriptor. Note that this
+ // needs to be done even for newly created objects, since they are
+ // being opened as well as created.
+
+ //
+ // Impersonate the client thread prior to doing an access check.
+ //
+
+ Status = I_RpcMapWin32Status(RpcImpersonateClient(0));
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+ }
+
+ //
+ // Map any Generic Access Types to Specific Access Types
+ //
+
+ RtlMapGenericMask(
+ &DesiredAccess,
+ &(LsapDbState.DbObjectTypes[ObjectTypeId].GenericMapping)
+ );
+
+ //
+ // Reconcile the desired access with the discretionary ACL
+ // of the Resultant Descriptor. Note that this operation is performed
+ // even if we are just creating the object since the object is to
+ // be opened.
+ //
+
+ Status = NtAccessCheckAndAuditAlarm(
+ &LsapState.SubsystemName,
+ ObjectHandle,
+ &LsapDbObjectTypeNames[ObjectTypeId],
+ (PUNICODE_STRING) ObjectInformation->ObjectAttributes.ObjectName,
+ ObjectInformation->ObjectAttributes.SecurityDescriptor,
+ DesiredAccess,
+ &(LsapDbState.DbObjectTypes[ObjectTypeId].GenericMapping),
+ FALSE,
+ (PACCESS_MASK) &(InternalHandle->GrantedAccess),
+ (PNTSTATUS) &AccessStatus,
+ (PBOOLEAN) &(InternalHandle->GenerateOnClose)
+ );
+
+ //
+ // Before checking the Status, stop impersonating the client and become
+ // our former self.
+ //
+
+ RevertStatus = I_RpcMapWin32Status(RpcRevertToSelf());
+
+ if (!NT_SUCCESS(RevertStatus)) {
+
+ LsapLogError(
+ "LsapDbRequestAccessObject: RpcRevertToSelf failed 0x%lx\n",
+ Status
+ );
+ }
+
+ //
+ // If the primary status code is a success status code, return the
+ // secondary status code. If this is alsoa success code, return the
+ // revert to self status.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = AccessStatus;
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = RevertStatus;
+ }
+ }
+
+ return Status;
+}
+
+NTSTATUS
+LsapDbRequestAccessNewObject(
+ IN OUT LSAPR_HANDLE ObjectHandle,
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that a desired set of accesses can be granted
+ to the handle that is opened when a new object is created.
+
+ It is important to note that the rules for granting accesses to the
+ handle that is open upon object creation are different from the rules
+ for granting accesses upon the opening of an existing object. For a new
+ object, the associated handle will be granted any subset of GENERIC_ALL
+ access desired and, if the creator has SE_SECURITY_PRIVILEGE, the handle
+ will be granted ACCESS_SYSTEM_SECURITY access if requested. If the
+ creator requests MAXIMUM_ALLOWED, the handle will be granted GENERIC_ALL.
+
+Arguments:
+
+ ObjectHandle - Handle to object. The handle will receive the
+ granted accesses if the call is successful.
+
+ ObjectInformation - Pointer to object's information. As a minimum, the
+ object's Security Descriptor must be set up.
+
+ DesiredAccess - Specifies a mask of the access types desired to the
+ object.
+
+ Options - Specifies optional actions to be taken
+
+ LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK - Omit the check for a BDC for
+ a create/update/delete operation on a local (non-replicated)
+ object such as a local secret.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Not all of the Desired Accessed can be
+ granted to the caller.
+
+ STATUS_BACKUP_CONTROLLER - A create, update or delete operation
+ is not allowed for a non-trusted client for this object on a BDC,
+ because the object is global to all DC's for a domain and is replicated.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ACCESS_MASK EffectiveDesiredAccess = DesiredAccess;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = InternalHandle->ObjectTypeId;
+ BOOLEAN WriteOperation = FALSE;
+ ULONG EffectiveOptions = Options | InternalHandle->Options;
+
+ //
+ // If the system is a Backup Domain Controller, disallow update
+ // operations for non-trusted callers except in special cases such
+ // as local non-replicated objects. In these special cases, the
+ // flag LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK will be already set
+ // in the handle Options.
+ //
+
+ WriteOperation = RtlAreAnyAccessesGranted(
+ LsapDbState.DbObjectTypes[ObjectTypeId].WriteOperations,
+ EffectiveDesiredAccess
+ );
+
+ if ((LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) &&
+ (!InternalHandle->Trusted) &&
+ WriteOperation &&
+ (!(EffectiveOptions & LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK))) {
+
+ Status = STATUS_BACKUP_CONTROLLER;
+ return Status;
+ }
+
+ //
+ // If MAXIMUM_ALLOWED is requested, add GENERIC_ALL
+ //
+
+ if (EffectiveDesiredAccess & MAXIMUM_ALLOWED) {
+
+ EffectiveDesiredAccess |= GENERIC_ALL;
+ }
+
+ //
+ // If ACCESS_SYSTEM_SECURITY is requested and we are a non-trusted
+ // client, check that we have SE_SECURITY_PRIVILEGE.
+ //
+
+ if ((EffectiveDesiredAccess & ACCESS_SYSTEM_SECURITY) &&
+ (!InternalHandle->Trusted)) {
+
+ Status = LsapRtlWellKnownPrivilegeCheck(
+ (PVOID)ObjectHandle,
+ TRUE,
+ SE_SECURITY_PRIVILEGE,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto RequestAccessNewObjectError;
+ }
+ }
+
+ //
+ // Make sure the caller can be given the requested access
+ // to the new object
+ //
+
+ InternalHandle->GrantedAccess = EffectiveDesiredAccess;
+
+ RtlMapGenericMask(
+ &InternalHandle->GrantedAccess,
+ &LsapDbState.DbObjectTypes[ObjectTypeId].GenericMapping
+ );
+
+ if ((LsapDbState.DbObjectTypes[ObjectTypeId].InvalidMappedAccess
+ &InternalHandle->GrantedAccess) != 0) {
+
+ Status = STATUS_ACCESS_DENIED;
+ goto RequestAccessNewObjectError;
+ }
+
+RequestAccessNewObjectFinish:
+
+ return(Status);
+
+RequestAccessNewObjectError:
+
+ goto RequestAccessNewObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbCloseObject(
+ IN PLSAPR_HANDLE ObjectHandle,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function closes (dereferences) a handle to an Lsa Database object.
+ If the reference count of the handle reduces to 0, the handle is freed.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ ObjectHandle - Pointer to handle to object from LsapDbOpenObject or
+ LsapDbCreateObject.
+
+ Options - Optional actions to be performed
+
+ LSAP_DB_VALIDATE_HANDLE - Verify that the handle is valid.
+
+ LSAP_DB_DEREFERENCE_CONTR - Dereference the Container Handle. Note
+ that the Container Handle was referenced when the subordinate
+ handle was created.
+
+ LSAP_DB_FREE_HANDLE - Free the handle whether or not the
+ Reference Count reaches zero.
+
+ LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES - Permit the handle provided
+ to be for a deleted object.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Dereference the object handle and free the handle if the reference count
+ // reaches zero. Optionally, the handle will be verified and/or freed
+ // and the container object handle dereferenced.
+ //
+
+ Status = LsapDbDereferenceObject(
+ ObjectHandle,
+ NullObject,
+ Options,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ *ObjectHandle = NULL;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbDeleteObject(
+ IN LSAPR_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function deletes an object from the Lsa Database.
+
+Arguments:
+
+ ObjectHandle - Handle to open object to be deleted.
+
+Return Value:
+
+ NTSTATUS - Standard NT Result Code.
+
+ STATUS_INVALID_HANDLE - Handle is not a valid handle to an open
+ object.
+
+ STATUS_ACCESS_DENIED - Handle does not specify DELETE access.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;
+ PUNICODE_STRING AttributeNames[LSAP_DB_MAX_ATTRIBUTES];
+ PUNICODE_STRING *NextAttributeName;
+ ULONG AttributeCount;
+ ULONG AttributeNumber;
+ LSAPR_TRUST_INFORMATION TrustInformation;
+
+ //
+ // Verify that the LSA Database is locked.
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // All object types have a Security Descriptor stored as the SecDesc
+ // attribute.
+ //
+
+ NextAttributeName = AttributeNames;
+ AttributeCount = 0;
+ *NextAttributeName = &LsapDbNames[SecDesc];
+
+ NextAttributeName++;
+ AttributeCount++;
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Check the other references to the object and mark all other handles
+ // invalid.
+ //
+
+ Status = LsapDbMarkDeletedObjectHandles( ObjectHandle, FALSE );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+ //
+ // Switch on object type
+ //
+
+ switch (Handle->ObjectTypeId) {
+
+ case PolicyObject:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ case TrustedDomainObject:
+
+ *NextAttributeName = &LsapDbNames[TrDmName];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[Sid];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[TrDmAcN];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[TrDmCtN];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[TrDmPxOf];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[TrDmCtEn];
+ NextAttributeName++;
+ AttributeCount++;
+
+ //
+ // Delete the object from the list of Trusted Domains
+ //
+
+ TrustInformation.Sid = Handle->Sid;
+ TrustInformation.Name = *((PLSAPR_UNICODE_STRING) &Handle->LogicalNameU);
+
+ Status = LsapDbDeleteTrustedDomainList( NULL, &TrustInformation );
+
+ break;
+
+ case AccountObject:
+
+ *NextAttributeName = &LsapDbNames[Sid];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[ActSysAc];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[Privilgs];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[QuotaLim];
+ NextAttributeName++;
+ AttributeCount++;
+
+ break;
+
+ case SecretObject:
+
+ *NextAttributeName = &LsapDbNames[CurrVal];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[OldVal];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[CupdTime];
+ NextAttributeName++;
+ AttributeCount++;
+
+ *NextAttributeName = &LsapDbNames[OupdTime];
+ NextAttributeName++;
+ AttributeCount++;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+ //
+ // Add Registry Transactions to delete each of the object's attributes.
+ //
+
+ for(AttributeNumber = 0; AttributeNumber < AttributeCount; AttributeNumber++) {
+
+ Status = LsapDbDeleteAttributeObject(
+ ObjectHandle,
+ AttributeNames[AttributeNumber]
+ );
+
+ //
+ // Ignore "attribute not found" errors. The object need not
+ // have all attributes set, or may be only partially created.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+ //
+ // Close the handle to the Registry Key representing the object.
+ // The Registry transaction package will open another handle with
+ // DELETE access to perform the deletion.
+ //
+
+ Status = NtClose(Handle->KeyHandle);
+
+ Handle->KeyHandle = NULL;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+ //
+ // Add a Registry Transaction to delete the object's Registry Key.
+ //
+
+ Status = RtlAddActionToRXact(
+ LsapDbState.RXactContext,
+ RtlRXactOperationDelete,
+ &((LSAP_DB_HANDLE) ObjectHandle)->PhysicalNameU,
+ 0L,
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteObjectError;
+ }
+
+DeleteObjectFinish:
+
+ //
+ // Decrement the count of objects of the given type.
+ //
+
+ LsapDbDecrementCountObject(
+ ((LSAP_DB_HANDLE) ObjectHandle)->ObjectTypeId
+ );
+
+ return (Status);
+
+DeleteObjectError:
+
+ goto DeleteObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbReferenceObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that a passed handle is valid, is for an
+ object of the specified type and has the specified accesses granted.
+ The handle's reference count is then incremented. If Lsa Database
+ locking is not requested, the Lsa Database must aready be locked.
+ If Lsa Database locking is requested, the Lsa Database must NOT be
+ locked.
+
+Arguments:
+
+ ObjectHandle - Pointer to handle to be validated and referenced.
+
+ DesiredAccess - Specifies the accesses that are desired. The function
+ returns an error if any of the specified accesses have not been
+ granted.
+
+ ObjectTypeId - Specifies the expected object type to which the handle
+ relates. An error is returned if this type does not match the
+ type contained in the handle.
+
+ Options - Specifies optional additional actions including database state
+ changes to be made, or actions not to be performed.
+
+ LSAP_DB_ACQUIRE_LOCK - Acquire the Lsa database lock. If this
+ flag is specified, the Lsa Database must NOT already be locked.
+ If this flag is not specified, the Lsa Database must already
+ be locked.
+
+ LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK - Acquire the Lsa Audit Log Queue
+ Lock.
+
+ LSAP_DB_START_TRANSACTION - Start an Lsa database transaction.
+
+ LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK - Omit check that local system
+ is not a Backup Domain Controller.
+
+ NOTE: There may be some Options (not database states) provided in the
+ ObjectHandle. These options augment those provided.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The handle could not be found.
+
+ STATUS_ACCESS_DENIED - Not all of the accesses specified have
+ been granted.
+
+ STATUS_OBJECT_TYPE_MISMATCH - The specified object type id does not
+ match the object type id contained in the handle.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
+ complete the command. An example is too many references to
+ the handle causing the count to overflow.
+
+ STATUS_BACKUP_CONTROLLER - A request to open a transaction has been
+ made by a non-trusted caller and the system is a Backup Domain
+ Controller. The LSA Database of a Backup Domain Controller
+ can only be updated by a trusted client, such as a replicator.
+
+ Result Codes from database transaction package.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ BOOLEAN GlobalSecret = FALSE;
+ ULONG States, EffectiveOptions;
+ ULONG ResetStates = 0;
+ BOOLEAN WriteOperation;
+
+ States = Options & LSAP_DB_STATE_MASK;
+
+ //
+ // Set the requested states before doing anything else. This ensures
+ // that the validity checks performed by this function are performed
+ // while the Lsa database is locked.
+ //
+
+ if (States != 0) {
+
+ Status = LsapDbSetStates( States );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ReferenceError;
+ }
+
+ if (States & LSAP_DB_START_TRANSACTION) {
+
+ ResetStates |= LSAP_DB_FINISH_TRANSACTION;
+ }
+
+ if (States & LSAP_DB_ACQUIRE_LOCK) {
+
+ ResetStates |= LSAP_DB_RELEASE_LOCK;
+ }
+
+ if (States & LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK) {
+
+ ResetStates |= LSAP_DB_RELEASE_LOG_QUEUE_LOCK;
+ }
+ }
+
+ //
+ // Search the list of handles for the given handle, validate the
+ // handle and verify that is for an object of the expected type.
+ // Augment the options passed in with those contained in the handle.
+ //
+
+ Status = LsapDbVerifyHandle( ObjectHandle, 0, ObjectTypeId );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ReferenceError;
+ }
+
+ //
+ // There may also be options set in the handle. Take these into
+ // account as well.
+ //
+
+ EffectiveOptions = Options | InternalHandle->Options;
+
+ //
+ // If the system is a Backup Domain Controller, disallow update
+ // operations for non-trusted callers except in special cases such
+ // as local non-replicated objects. In these special cases where update
+ // is allowed, the flag LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK will be already set
+ // in the handle Options.
+ //
+
+ WriteOperation = RtlAreAnyAccessesGranted(
+ LsapDbState.DbObjectTypes[InternalHandle->ObjectTypeId].WriteOperations,
+ DesiredAccess
+ );
+
+ if ((LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) &&
+ (!InternalHandle->Trusted) &&
+ WriteOperation &&
+ (!(EffectiveOptions & LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK))) {
+
+ Status = STATUS_BACKUP_CONTROLLER;
+ goto ReferenceError;
+ }
+
+ //
+ // If the handle is not Trusted, verify that the desired accesses have been granted
+ //
+
+ if (!(InternalHandle->Trusted)) {
+
+ if (!RtlAreAllAccessesGranted( InternalHandle->GrantedAccess, DesiredAccess )) {
+
+ Status = STATUS_ACCESS_DENIED;
+ goto ReferenceError;
+ }
+ }
+
+ //
+ // Reference the handle
+ //
+
+ if (InternalHandle->ReferenceCount == LSAP_DB_MAXIMUM_REFERENCE_COUNT) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ReferenceError;
+ }
+
+ InternalHandle->ReferenceCount++;
+ return (Status);
+
+ReferenceError:
+
+ //
+ // Unset the states in the correct order. If a database transaction
+ // was started by this routine, it will be aborted.
+ //
+
+ Status = LsapDbResetStates(
+ ObjectHandle,
+ ResetStates,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbDereferenceObject(
+ IN OUT PLSAPR_HANDLE ObjectHandle,
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
+ IN ULONG Options,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
+ IN NTSTATUS PreliminaryStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function dereferences a handle, optionally validating it first.
+ If the Reference Count in the handle goes to 0, the handle is freed.
+ The Lsa Database may optionally be unlocked by this function. It
+ must be locked before calling this function.
+
+Arguments:
+
+ ObjectHandle - Pointer to handle to be dereferenced. If the reference
+ count reaches 0, NULL is returned in this location.
+
+ ObjectTypeId - Expected type of object. This parameter is ignored
+ if ValidateHandle is set to FALSE.
+
+ Options - Specifies optional additional actions to be performed including
+ Lsa Database states to be cleared.
+
+ LSAP_DB_VALIDATE_HANDLE - Validate the handle.
+
+ LSAP_DEREFERENCE_CONTR - Dereference the container object
+
+ LSAP_DB_FREE_HANDLE - Free the handle whether or not the
+ Reference Count reaches zero. If LSAP_DB_DEREFERENCE_CONTR
+ is also specified, the container handle Reference Count is
+ decremented by the reference count in the handle being deleted.
+
+ LSAP_DB_FINISH_TRANSACTION - A database transaction was started
+ and must be concluded. Conclude the current Lsa Database
+ transaction by applying or aborting it depending on the
+ final Status.
+
+ LSAP_DB_RELEASE_LOCK - The Lsa database lock was acquired and
+ should be released.
+
+ LSAP_DB_RELEASE_LOG_QUEUE_LOCK - The Lsa Audit Log Queue Lock
+ was acquired and should be released.
+
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
+ Replicator of the change.
+
+ LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES - Permit the handle provided
+ to be for a deleted object.
+
+ NOTE: There may be some Options (not database states) provided in the
+ ObjectHandle. These options augment those provided.
+
+ PreliminaryStatus = Current Result Code.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The handle could not be found.
+
+ STATUS_OBJECT_TYPE_MISMATCH - The specified object type id does not
+ match the object type id contained in the handle.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus, TmpStatus;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) *ObjectHandle;
+ BOOLEAN DecrementCount = TRUE;
+ ULONG EffectiveOptions;
+ ULONG ReferenceCount = 0;
+
+ Status = PreliminaryStatus;
+ SecondaryStatus = STATUS_SUCCESS;
+
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // There may also be options set in the handle. Take these into
+ // account as well.
+ //
+
+ EffectiveOptions = Options | InternalHandle->Options;
+
+ //
+ // If validating, lookup the handle and match the type.
+ //
+
+ if (EffectiveOptions & LSAP_DB_VALIDATE_HANDLE) {
+
+ SecondaryStatus = LsapDbVerifyHandle(
+ *ObjectHandle,
+ EffectiveOptions,
+ ObjectTypeId
+ );
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ DecrementCount = FALSE;
+ goto DereferenceObjectError;
+ }
+ }
+
+ //
+ // Dereference the container handle if so requested
+ //
+
+ if (EffectiveOptions & LSAP_DB_DEREFERENCE_CONTR) {
+
+ if (InternalHandle->ContainerHandle != NULL) {
+ //
+ // Dereference the container object.
+ //
+
+ Status = LsapDbDereferenceObject(
+ (PLSAPR_HANDLE) &InternalHandle->ContainerHandle,
+ NullObject,
+ 0,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ }
+ }
+
+
+DereferenceObjectFinish:
+
+ //
+ // Decrement the Reference Count. If it becomes zero, free the
+ // handle. If explicitly requested to free the handle (regardless of
+ // the Reference Count), force the Reference Count to zero prior to
+ // freeing.
+ //
+
+ if (DecrementCount) {
+
+ if (Options & LSAP_DB_FREE_HANDLE) {
+
+ InternalHandle->ReferenceCount = (ULONG) 1;
+ }
+
+ (InternalHandle->ReferenceCount)--;
+ ReferenceCount = InternalHandle->ReferenceCount;
+
+ }
+
+
+ //
+ // This must happen after the reference count is adjusted, as it the one
+ // that will unlock the database.
+ //
+
+ if (NT_SUCCESS(SecondaryStatus))
+ {
+ Status = LsapDbResetStates(
+ *ObjectHandle,
+ EffectiveOptions,
+ SecurityDbDeltaType,
+ Status
+ );
+
+ }
+
+ //
+ // This has to happen after resetting states because resetting
+ // requires the handle to be present.
+ //
+
+ if (DecrementCount && (ReferenceCount == 0)) {
+
+ TmpStatus = NtCloseObjectAuditAlarm (
+ &LsapState.SubsystemName,
+ *ObjectHandle,
+ InternalHandle->GenerateOnClose
+ );
+
+ if (!NT_SUCCESS( TmpStatus )) {
+ LsapAuditFailed();
+ }
+
+ LsapDbFreeHandle( *ObjectHandle );
+
+ *ObjectHandle = NULL;
+ }
+
+
+
+ return( Status );
+
+DereferenceObjectError:
+
+ if (NT_SUCCESS(Status) && !NT_SUCCESS(SecondaryStatus)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto DereferenceObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbReadAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PUNICODE_STRING AttributeNameU,
+ IN OPTIONAL PVOID AttributeValue,
+ IN OUT PULONG AttributeValueLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads the value of an attribute of an open LSA Database object.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called and the supplied ObjectHandle must be valid.
+
+Arguments:
+
+ ObjectHandle - LSA Handle to object. This must be valid.
+
+ AttributeNameU - Pointer to Unicode name of attribute
+
+ AttributeValue - Pointer to buffer to receive attribute's value. This
+ parameter may be NULL if the input AttributeValueLength is zero.
+
+ AttributeValueLength - Pointer to variable containing on input the size of
+ attribute value buffer and on output the size of the attributes's
+ value. A value of zero may be specified to indicate that the size of
+ the attribute's value is unknown.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_BUFFER_OVERFLOW - This warning is returned if the specified
+ attribute value length is non-zero and too small for the
+ attribute's value.
+--*/
+
+{
+ //
+ // The LSA Database is implemented as a subtree of the Configuration
+ // Registry. In this implementation, Lsa Database objects correspond
+ // to Registry keys and "attributes" and their "values" correspond to
+ // Registry "subkeys" and "values" of the Registry key representing the
+ // object.
+ //
+
+ NTSTATUS Status, SecondaryStatus;
+ ULONG SubKeyValueActualLength;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE SubKeyHandle = NULL;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Reading an attribute of an object is simpler than writing one,
+ // because the Registry Transaction package is not used. Since an
+ // attribute is stored as the value of a subkey of the object's
+ // Registry Key, we can simply call the Registry API RtlpNtReadKey
+ // specifying the relative name of the subkey and the parent key's
+ // handle.
+ //
+ // Prior to opening the subkey in the Registry, setup ObjectAttributes
+ // containing the SubKey name and the Registry Handle for the LSA Database
+ // Root.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ AttributeNameU,
+ OBJ_CASE_INSENSITIVE,
+ InternalHandle->KeyHandle,
+ NULL
+ );
+
+ //
+ // Open the subkey
+ //
+
+ Status = RtlpNtOpenKey(
+ &SubKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0L
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ SubKeyHandle = NULL; //For error processing
+ return(Status);
+ }
+
+ //
+ // Now query the size of the buffer required to read the subkey's
+ // value.
+ //
+
+ SubKeyValueActualLength = *AttributeValueLength;
+
+ Status = RtlpNtQueryValueKey(
+ SubKeyHandle,
+ NULL,
+ NULL,
+ &SubKeyValueActualLength,
+ NULL
+ );
+
+ if ((Status == STATUS_BUFFER_OVERFLOW) || NT_SUCCESS(Status)) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ goto ReadAttError;
+ }
+
+ //
+ // If a NULL buffer parameter has been supplied or the size of the
+ // buffer given is 0, this is just a size query.
+ //
+
+ if (!ARGUMENT_PRESENT(AttributeValue) || *AttributeValueLength == 0) {
+
+ *AttributeValueLength = SubKeyValueActualLength;
+ Status = STATUS_SUCCESS;
+ goto ReadAttError;
+
+ } else if(*AttributeValueLength < SubKeyValueActualLength) {
+
+ *AttributeValueLength = SubKeyValueActualLength;
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto ReadAttError;
+ }
+
+ //
+ // Supplied buffer is large enough to hold the SubKey's value.
+ // Query the value.
+ //
+
+ Status = RtlpNtQueryValueKey(
+ SubKeyHandle,
+ NULL,
+ AttributeValue,
+ &SubKeyValueActualLength,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ReadAttError;
+ }
+
+ //
+ // Return the length of the Sub Key.
+ //
+
+ *AttributeValueLength = SubKeyValueActualLength;
+
+ReadAttFinish:
+
+ //
+ // If necessary, close the Sub Key
+ //
+
+ if (SubKeyHandle != NULL) {
+
+ SecondaryStatus = NtClose( SubKeyHandle );
+
+#if DBG
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ DbgPrint(
+ "LsapDbReadAttributeObject: NtClose failed 0x%lx\n",
+ Status
+ );
+ }
+
+#endif // DBG
+
+ }
+
+ return(Status);
+
+ReadAttError:
+
+ goto ReadAttFinish;
+}
+
+
+NTSTATUS
+LsapDbWriteAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PUNICODE_STRING AttributeNameU,
+ IN PVOID AttributeValue,
+ IN ULONG AttributeValueLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine writes the value of an attribute of an open LSA Database
+ object. A Database transaction must already be open: the write is
+ appended to the transaction log.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ ObjectHandle - Lsa Handle of open object.
+
+ AttributeNameU - Pointer to Unicode string containing the name of the
+ attribute whose value is to be written.
+
+ AttributeValue - Pointer to buffer containing attribute's value. If NULL
+ is specified for this parameter, AttributeValueLength must be 0.
+
+ AttributeValueLength - Contains the size of attribute value buffer to be
+ written. 0 may be specified, indicating that the attribute is to be
+ deleted.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The attribute was successfully added to the
+ transaction log.
+
+ STATUS_INVALID_PARAMETER - AttributeValue is NULL but the
+ AttributeValueLength value is not 0.
+
+ Errors from the Registry Transaction Package.
+--*/
+
+{
+ //
+ // The LSA Database is implemented as a subtree of the Configuration
+ // Registry. In this implementation, Lsa Database objects correspond
+ // to Registry keys and "attributes" and their "values" correspond to
+ // Registry "subkeys" and "values" of the Registry key representing the
+ // object.
+ //
+
+ NTSTATUS Status;
+ UNICODE_STRING PhysicalSubKeyNameU;
+
+ PhysicalSubKeyNameU.Buffer = NULL;
+
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // If the attribute value is null, verify that the AttributeValueLength
+ // field is 0.
+ //
+
+ if (!ARGUMENT_PRESENT(AttributeValue)) {
+
+ if (AttributeValueLength != 0) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto WriteAttributeObjectError;
+ }
+ }
+
+ //
+ // Writing an object attribute's value is more complex than reading
+ // one because the Registry Transaction package is called instead of
+ // calling the Registry API directly. Since the transaction package
+ // expects to perform its own open of the target subkey representing
+ // the attribute (when a transaction commit is finally done) using a
+ // name relative to the LSA Database Registry Transaction Key (which
+ // we call the Physical Name within the LSA Database code). The
+ // Registry Key handle contained in the object handle is therefore
+ // not used by the present routine. Instead, we need to construct the
+ // Physical Name the sub key and pass it together with the LSA Database
+ // Registry transaction key handle on the Registry transaction API
+ // call. The Physical Name of the subkey is constructed by
+ // concatenating the Physical Object Name stored in the object handle
+ // with a "\" and the given sub key name.
+ //
+
+ Status = LsapDbLogicalToPhysicalSubKey(
+ ObjectHandle,
+ &PhysicalSubKeyNameU,
+ AttributeNameU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto WriteAttributeObjectError;
+ }
+
+ //
+ // Now log the sub key write as a Registry Transaction
+ //
+
+ Status = RtlAddActionToRXact(
+ LsapDbState.RXactContext,
+ RtlRXactOperationSetValue,
+ &PhysicalSubKeyNameU,
+ 0L,
+ AttributeValue,
+ AttributeValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto WriteAttributeObjectError;
+ }
+
+WriteAttributeObjectFinish:
+
+ //
+ // If necessary, free the Unicode String buffer allocated by
+ // LsapDbLogicalToPhysicalSubKey;
+ //
+
+ if (PhysicalSubKeyNameU.Buffer != NULL) {
+
+ RtlFreeUnicodeString(&PhysicalSubKeyNameU);
+ }
+
+ return(Status);
+
+WriteAttributeObjectError:
+
+ goto WriteAttributeObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbWriteAttributesObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PLSAP_DB_ATTRIBUTE Attributes,
+ IN ULONG AttributeCount
+ )
+
+/*++
+
+Routine Description:
+
+ This routine writes the values of one or more attributes of an open LSA
+ Database object. A Database transaction must already be open: the write
+ is appended to the transaction log. The attribute names specified are
+ assumed to be consistent with the object type and the values supplied
+ are assumed to be valid.
+
+ WARNINGS: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ ObjectHandle - Lsa Handle of open object.
+
+ Attributes - Pointer to an array of Attribute Information blocks each
+ containing pointers to the attribute's Unicode Name, the value
+ to be stored, and the length of the value in bytes.
+
+ AttributeCount - Count of the attributes to be written, equivalently,
+ this is the number of elements of the array pointed to by Attributes.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Index;
+
+ for(Index = 0; Index < AttributeCount; Index++) {
+
+ Status = LsapDbWriteAttributeObject(
+ ObjectHandle,
+ Attributes[Index].AttributeName,
+ Attributes[Index].AttributeValue,
+ Attributes[Index].AttributeValueLength
+ );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbReadAttributesObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN OUT PLSAP_DB_ATTRIBUTE Attributes,
+ IN ULONG AttributeCount
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads the values of one or more attributes of an open LSA
+ Database object. A Database transaction must already be open: the write
+ is appended to the transaction log. The attribute names specified are
+ assumed to be consistent with the object type and the values supplied
+ are assumed to be valid. This routine will allocate memory via
+ MIDL_user_allocate for buffers which will receive attribute values if
+ requested. This memory must be freed after use by calling MIDL_User_free
+ after use.
+
+ WARNINGS: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ ObjectHandle - Lsa Handle of open object.
+
+ Attributes - Pointer to an array of Attribute Information blocks each
+ containing pointers to the attribute's Unicode Name, an optional
+ pointer to a buffer that will receive the value and an optional
+ length of the value expected in bytes.
+
+ If the AttributeValue field in this structure is specified as non-NULL,
+ the attribute's data will be returned in the specified buffer. In
+ this case, the AttributeValueLength field must specify a sufficiently
+ large buffer size in bytes. If the specified size is too small,
+ a warning is returned and the buffer size required is returned in
+ AttributeValueLength.
+
+ If the AttributeValue field in this structure is NULL, the routine
+ will allocate memory for the attribute value's buffer, via MIDL_user_allocate(). If
+ the AttributeValueLength field is non-zero, the number of bytes specified
+ will be allocated. If the size of buffer allocated is too small to
+ hold the attribute's value, a warning is returned. If the
+ AttributeValuelength field is 0, the routine will first query the size
+ of buffer required and then allocate its memory.
+
+ In all success cases and buffer overflow cases, the
+ AttributeValueLength is set upon exit to the size of data required.
+
+ AttributeCount - Count of the attributes to be read, equivalently,
+ this is the number of elements of the array pointed to by Attributes.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - One or more of the specified
+ attributes do not exist. In this case, the attribute information
+ AttributeValue, AttributeValueLength fields are zeroised. Note
+ that an attempt will be made to read all of the supplied
+ attributes, even if one of them is not found.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_ATTRIBUTE NextAttribute = NULL;
+ BOOLEAN MemoryToFree = FALSE;
+ ULONG MemoryToFreeCount = 0;
+
+ for (NextAttribute = Attributes;
+ NextAttribute < &Attributes[AttributeCount];
+ NextAttribute++) {
+
+ NextAttribute->MemoryAllocated = FALSE;
+
+ // If an explicit buffer pointer is given, verify that the length
+ // specified is non-zero and attempt to use that buffer.
+ //
+
+ if (NextAttribute->AttributeValue != NULL) {
+
+ if (NextAttribute->AttributeValueLength == 0) {
+
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ Status = LsapDbReadAttributeObject(
+ ObjectHandle,
+ NextAttribute->AttributeName,
+ (PVOID) NextAttribute->AttributeValue,
+ (PULONG) &NextAttribute->AttributeValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If the attribute was not found, set the AttributeValue
+ // and AttributeValueLength fields to NULL and continue.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ break;
+ }
+
+ NextAttribute->AttributeValue = NULL;
+ NextAttribute->AttributeValueLength = 0;
+ }
+
+ continue;
+ }
+
+ //
+ // No output buffer pointer has been given. If a zero buffer
+ // size is given, query size of memory required. Since the
+ // buffer length is 0, STATUS_SUCCESS should be returned rather
+ // than STATUS_BUFFER_OVERFLOW.
+ //
+
+ if (NextAttribute->AttributeValueLength == 0) {
+
+ Status = LsapDbReadAttributeObject(
+ ObjectHandle,
+ NextAttribute->AttributeName,
+ NULL,
+ (PULONG) &NextAttribute->AttributeValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If the attribute was not found, set the AttributeValue
+ // and AttributeValueLength fields to NULL and continue.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ break;
+ }
+
+ NextAttribute->AttributeValue = NULL;
+ NextAttribute->AttributeValueLength = 0;
+ continue;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // If the attribute value size needed is 0, return NULL pointer
+ //
+
+ if (NextAttribute->AttributeValueLength == 0) {
+
+ NextAttribute->AttributeValue = NULL;
+ continue;
+ }
+
+ //
+ // Allocate memory for the buffer.
+ //
+
+ NextAttribute->AttributeValue =
+ MIDL_user_allocate(NextAttribute->AttributeValueLength);
+
+ if (NextAttribute->AttributeValue == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ NextAttribute->MemoryAllocated = TRUE;
+ MemoryToFree = TRUE;
+ MemoryToFreeCount++;
+
+ //
+ // Now read the attribute into the buffer.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ ObjectHandle,
+ NextAttribute->AttributeName,
+ (PVOID) NextAttribute->AttributeValue,
+ (PULONG) &NextAttribute->AttributeValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ReadAttributesError;
+ }
+
+ReadAttributesFinish:
+
+ return(Status);
+
+ReadAttributesError:
+
+ //
+ // If memory was allocated for any values read, it must be freed.
+ //
+
+ if (MemoryToFree) {
+
+ for (NextAttribute = &Attributes[0];
+ (MemoryToFreeCount > 0) &&
+ (NextAttribute < &Attributes[AttributeCount]);
+ NextAttribute++) {
+
+ if (NextAttribute->MemoryAllocated) {
+
+ MIDL_user_free( NextAttribute->AttributeValue );
+ NextAttribute->AttributeValue = NULL;
+ MemoryToFreeCount--;
+ }
+ }
+ }
+
+ goto ReadAttributesFinish;
+}
+
+
+NTSTATUS
+LsapDbDeleteAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PUNICODE_STRING AttributeNameU
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes an attribute of an open LSA Database object.
+ A Database transaction must already be open: the delete actions are
+ appended to the transaction log.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+ The LSA Database is implemented as a subtree of the Configuration
+ Registry. In this implementation, Lsa Database objects correspond
+ to Registry keys and "attributes" and their "values" correspond to
+ Registry "subkeys" and "values" of the Registry key representing the
+ object.
+
+Arguments:
+
+ ObjectHandle - Lsa Handle of open object.
+
+ AttributeNameU - Pointer to Unicode string containing the name of the
+ attribute whose value is to be written.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING PhysicalSubKeyNameU;
+ ULONG AttributeLength = 0;
+
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // The Registry code will actually create a key if one does not exist, so
+ // probe for the existence of the key first.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ ObjectHandle,
+ AttributeNameU,
+ NULL,
+ &AttributeLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteAttributeObjectError;
+ }
+
+ //
+ // We need to construct the Physical Name the sub key relative
+ // to the LSA Database root node in the Registry. This is done by
+ // concatenating the Physical Object Name stored in the object handle with
+ // a "\" and the given sub key name.
+ //
+
+ Status = LsapDbLogicalToPhysicalSubKey(
+ ObjectHandle,
+ &PhysicalSubKeyNameU,
+ AttributeNameU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteAttributeObjectError;
+ }
+
+ //
+ // Now log the sub key write as a Registry Transaction
+ //
+
+ Status = RtlAddActionToRXact(
+ LsapDbState.RXactContext,
+ RtlRXactOperationDelete,
+ &PhysicalSubKeyNameU,
+ 0L,
+ NULL,
+ 0
+ );
+
+ RtlFreeUnicodeString(&PhysicalSubKeyNameU);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto DeleteAttributeObjectError;
+ }
+
+DeleteAttributeObjectFinish:
+
+ return(Status);
+
+DeleteAttributeObjectError:
+
+ //
+ // Add any cleanup required on error paths only here.
+ //
+
+ goto DeleteAttributeObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbReferencesObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ OUT PULONG ReferenceCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the Reference Count for the object. This is
+ the sum of the Reference Counts found in each open handle. The LSA
+ Database must be locked before calling this function.
+
+Arguments:
+
+ ObjectHandle - Handle to the object.
+
+ ReferenceCount - Receives the Reference Count for the object.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - Specified handle is invalid.
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Verify that the Lsa Database is locked.
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ Status = LsapDbReferencesHandle( ObjectHandle, ReferenceCount );
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbNotifyChangeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType
+ )
+
+/*++
+
+Routine Description:
+
+ This function notifies the LSA Database Replicator (if any) of a
+ change to an object. Change notifications for Secret objects specify
+ that replication of the change should occur immediately.
+
+ WARNING! All parameters passed to this routine are assumed valid.
+ No checking will be done.
+
+Arguments:
+
+ ObjectHandle - Handle to an LSA object. This is expected to have
+ already been validated.
+
+ SecurityDbDeltaType - Specifies the type of change being made. The
+ following values only are relevant:
+
+ SecurityDbNew - Indicates that a new object has been created.
+ SecurityDbDelete - Indicates that an object is being deleted.
+ SecurityDbChange - Indicates that the attributes of an object
+ are being changed, including creation or deletion of
+ attributes.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INVALID_HANDLE - The specified handle is invalid. This
+ error is only returned if the Object Type Id in the handle
+ is invalid.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ SECURITY_DB_OBJECT_TYPE ObjectType;
+ UNICODE_STRING ObjectName;
+ PSID ObjectSid = NULL;
+ ULONG ObjectRid = 0;
+ UCHAR SubAuthorityCount;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ BOOLEAN ReplicateImmediately = FALSE;
+
+ ObjectName.Buffer = NULL;
+ ObjectName.Length = ObjectName.MaximumLength = 0;
+
+ //
+ // If notifications are disabled, just exit.
+ //
+
+ if (!LsapDbState.ReplicatorNotificationEnabled) {
+
+ goto NotifyChangeObjectFinish;
+ }
+
+ //
+ // If the system is a Backup Domain Controller, don't notify the
+ // replicator of any changes.
+ //
+
+ if (LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole == PolicyServerRoleBackup) {
+
+ goto NotifyChangeObjectFinish;
+ }
+
+ //
+ // Convert the Lsa Database Object Type to a Database Delta Type.
+ //
+
+ switch (InternalHandle->ObjectTypeId) {
+
+ case PolicyObject:
+
+ ObjectType = SecurityDbObjectLsaPolicy;
+ break;
+
+ case AccountObject:
+
+ ObjectType = SecurityDbObjectLsaAccount;
+ break;
+
+ case TrustedDomainObject:
+
+ ObjectType = SecurityDbObjectLsaTDomain;
+ break;
+
+ case SecretObject:
+
+ ObjectType = SecurityDbObjectLsaSecret;
+ ReplicateImmediately = TRUE;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_HANDLE;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto NotifyChangeObjectError;
+ }
+
+ //
+ // Get the Name or Sid of the object from its handle. If the object
+ // is of a type such as SecretObject that is accessed by Name, then
+ // the object's externally known name is equal to its internal
+ // Logical Name contained in the handle.
+ //
+
+ if (LsapDbAccessedBySidObject( InternalHandle->ObjectTypeId )) {
+
+ ObjectSid = InternalHandle->Sid;
+ SubAuthorityCount = *RtlSubAuthorityCountSid( ObjectSid );
+ ObjectRid = *RtlSubAuthoritySid( ObjectSid, SubAuthorityCount -1 );
+
+ } else if (LsapDbAccessedByNameObject( InternalHandle->ObjectTypeId )) {
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ &ObjectName,
+ &InternalHandle->LogicalNameU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto NotifyChangeObjectError;
+ }
+
+ } else {
+
+ //
+ // Currently, an object is either accessed by Sid or by Name, so
+ // something is wrong if both of the above chacks have failed.
+ //
+
+ Status = STATUS_INVALID_HANDLE;
+
+ goto NotifyChangeObjectError;
+ }
+
+ //
+ // Notify the LSA Database Replicator of the change.
+ //
+
+ Status = I_NetNotifyDelta (
+ SecurityDbLsa,
+ LsapDbState.PolicyModificationInfo.ModifiedId,
+ SecurityDbDeltaType,
+ ObjectType,
+ ObjectRid,
+ ObjectSid,
+ &ObjectName,
+ ReplicateImmediately,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto NotifyChangeObjectError;
+ }
+
+NotifyChangeObjectFinish:
+
+ //
+ // If we allocated memory for the Object Name Unicode buffer, free it.
+ //
+
+ if (ObjectName.Buffer != NULL) {
+
+ MIDL_user_free( ObjectName.Buffer );
+ }
+
+ //
+ // Suppress any error and return STATUS_SUCCESS. Currently, there is
+ // no meaningful action an LSA client of this routine can take.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ return(Status);
+
+NotifyChangeObjectError:
+
+ goto NotifyChangeObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbVerifyInformationObject(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function verifies that the information specified in passed
+ ObjectInformation is syntactically valid.
+
+Arguments:
+
+ ObjectInformation - Pointer to information describing this object. The
+ following information items must be specified:
+
+ o Object Type Id
+ o Object Logical Name (as ObjectAttributes->ObjectName, a pointer to
+ a Unicode string)
+ o Container object handle (for any object except the root Policy object).
+
+ All other fields in ObjectAttributes portion of ObjectInformation
+ such as SecurityDescriptor are ignored.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_PARAMETER - Invalid object information given
+ - ObjectInformation is NULL
+ - Object Type Id is out of range
+ - No Logical Name pointer given
+ - Logical Name not a pointer to a Unicode String (TBS)
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
+
+ //
+ // Verify that ObjectInformation is given
+ //
+
+ if (!ARGUMENT_PRESENT(ObjectInformation)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Validate the Object Type Id. It must be in range.
+ //
+
+ if (!LsapDbIsValidTypeObject(ObjectTypeId)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Verify that a Logical Name is given. A pointer to a Unicode string
+ // is expected.
+ //
+
+ if (!ARGUMENT_PRESENT(ObjectInformation->ObjectAttributes.ObjectName)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbSidToLogicalNameObject(
+ IN PSID Sid,
+ OUT PUNICODE_STRING LogicalNameU
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates the Logical Name (Internal LSA Database Name)
+ of an object from its Sid. Currently, only the Relative Id (lowest
+ sub-authority) is used due to Registry and hence Lsa Database limits
+ on name components to 8 characters. The Rid is extracted and converted
+ to an 8-digit Unicode Integer.
+
+Arguments:
+
+ Sid - Pointer to the Sid to be looked up. It is the caller's
+ responsibility to ensure that the Sid has valid syntax.
+
+ LogicalNameU - Pointer to a Unicode String structure that will receive
+ the Logical Name. Note that memory for the string buffer in this
+ Unicode String will be allocated by this routine if successful. The
+ caller must free this memory after use by calling RtlFreeUnicodeString.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Status code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to allocate buffer for Unicode String name.
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // First, verify that the given Sid is valid
+ //
+
+ if (!RtlValidSid( Sid )) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+
+ Status = RtlConvertSidToUnicodeString( LogicalNameU, Sid, TRUE);
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbGetNamesObject(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ OUT OPTIONAL PUNICODE_STRING LogicalNameU,
+ OUT OPTIONAL PUNICODE_STRING PhysicalNameU
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the Logical and/or Physical Names of an object
+ given an object information buffer. Memory will be allocated for
+ the Unicode String Buffers that will receive the name(s).
+
+ The Logical Name of an object is the path of the object within the
+ LSA Database relative to its Classifying Directory. The Logical Name
+ of an object is implemntation-dependent and on current implementations
+ is equal to one of the following depending on object type:
+
+ o The External Name of the object (if any)
+ o The Relative Id (lowest sub-authority) in the object's Sid (if any)
+ converted to an 8-digit integer, including leading 0's added as
+ padding.
+
+ The Physical Name of an object is the full path of the object relative
+ to the root ot the Database. It is computed by concatenating the Physical
+ Name of the Container Object (if any), the Classifying Directory
+ corresponding to the object type id, and the Logical Name of the
+ object.
+
+ <Physical Name of Object> =
+ [<Physical Name of Container Object> "\"]
+ [<Classifying Directory> "\"] <Logical Name of Object>
+
+ If there is no Container Object (as in the case of the Policy object)
+ the <Physical Name of Container Object> and following \ are omitted.
+ If there is no Classifying Directory (as in the case of the Policy object)
+ the <Classifying Directory> and following \ are omitted. If neither
+ Container Object not Classifying Directory exist, the Logical and Physical
+ names coincide.
+
+ Note that memory is allocated by this routine for the output
+ Unicode string buffer(s). When the output Unicode String(s) are no
+ longer needed, the memory must be freed by call(s) to
+ RtlFreeUnicodeString().
+
+ Example of Physical Name computation:
+
+ Consider the user or group account object ScottBi
+
+ Container Object Logical Name: Policy
+ Container Object Physical Name: Policy (no Classifying Directory or
+ Container Object exists)
+ Classifying Directory for ScottBi: Accounts
+ Logical Name of Object: ScottBi
+ Physical Name of Object Policy\Accounts\ScottBi
+
+ Note that the Physical Name is exactly the Registry path relative to
+ the Security directory.
+
+ WARNING: The Lsa Database must be in the locked state when this function
+ is called.
+
+Arguments:
+
+ ObjectInformation - Pointer to object information containing as a minimum
+ the object's Logical Name, Container Object's handle and object type
+ id.
+
+ LogicalNameU - Optional pointer to Unicode String structure which will
+ receive the Logical Name of the object. A buffer will be allocated
+ by this routine for the name text. This memory must be freed when no
+ longer needed by calling RtlFreeUnicodeString() wiht a pointer such
+ as LogicalNameU to the Unicode String structure.
+
+ PhysicalNameU - Optional pointer to Unicode String structure which will
+ receive the Physical Name of the object. A buffer will be allocated by
+ this routine for the name text. This memory must be freed when no
+ longer needed by calling RtlFreeUnicodeString() with a pointer such as
+ PhysicalNameU to the Unicode String structure.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
+ allocate the name string buffer for the Physical Name or
+ Logical Name.
+--*/
+
+{
+ NTSTATUS Status;
+
+ PUNICODE_STRING ContainerPhysicalNameU = NULL;
+ PUNICODE_STRING ClassifyingDirU = NULL;
+ UNICODE_STRING IntermediatePath1U;
+ PUNICODE_STRING JoinedPath1U = &IntermediatePath1U;
+ LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
+ POBJECT_ATTRIBUTES ObjectAttributes = &ObjectInformation->ObjectAttributes;
+
+ UNICODE_STRING TempLogicalNameU;
+
+ //
+ // Initialize
+ //
+
+ RtlInitUnicodeString( &IntermediatePath1U, NULL );
+ RtlInitUnicodeString( &TempLogicalNameU, NULL );
+
+ //
+ // Verify that the LSA Database is locked
+ //
+
+ ASSERT (LsapDbIsLocked());
+
+ //
+ // Capture the Logical Name of the object into permanent memory.
+ //
+
+ Status = LsapRtlCopyUnicodeString(
+ &TempLogicalNameU,
+ (PUNICODE_STRING)
+ ObjectInformation->ObjectAttributes.ObjectName,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GetNamesError;
+ }
+
+ //
+ // If the Logical Name of the object is requested, return this.
+ //
+
+ if (ARGUMENT_PRESENT(LogicalNameU)) {
+
+ *LogicalNameU = TempLogicalNameU;
+ }
+
+ //
+ // If the Physical Name of the object is not required, just return.
+ //
+
+ if (!ARGUMENT_PRESENT(PhysicalNameU)) {
+
+ goto GetNamesFinish;
+ }
+
+ //
+ // The Physical Name of the object is requested. Construct this
+ // in stages. First, get the Container Object Physical Name from
+ // the handle stored inside ObjectAttributes.
+ //
+
+ if (ObjectAttributes->RootDirectory != NULL) {
+
+ ContainerPhysicalNameU =
+ &(((LSAP_DB_HANDLE)
+ ObjectAttributes->RootDirectory)->PhysicalNameU);
+ }
+
+ //
+ // Next, get the Classifying Directory name appropriate to the
+ // object type.
+ //
+
+ if (LsapDbContDirs[ObjectTypeId].Length != 0) {
+
+ ClassifyingDirU = &LsapDbContDirs[ObjectTypeId];
+ }
+
+ //
+ // Now join the Physical Name of the Container Object and Classifying
+ // Directory together. If there is no Container Object and no
+ // Classifying Directory, just set the result to NULL.
+ //
+
+ if (ContainerPhysicalNameU == NULL && ClassifyingDirU == NULL) {
+
+ JoinedPath1U = NULL;
+
+ } else {
+
+ Status = LsapDbJoinSubPaths(
+ ContainerPhysicalNameU,
+ ClassifyingDirU,
+ JoinedPath1U
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GetNamesError;
+ }
+ }
+
+ //
+ // Now join the Physical Name of the Containing Object, Classifying
+ // Directory and Logical Name of the object together. Note that
+ // JoinedPath1U may be NULL, but LogicalNameU is never NULL.
+ //
+
+ Status = LsapDbJoinSubPaths(
+ JoinedPath1U,
+ &TempLogicalNameU,
+ PhysicalNameU
+ );
+ if (JoinedPath1U != NULL) {
+
+ RtlFreeUnicodeString( JoinedPath1U );
+ JoinedPath1U = NULL; // so we don't try to free it again
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto GetNamesError;
+ }
+
+ goto GetNamesFinish;
+
+GetNamesError:
+
+ //
+ // If necessary, free any string buffer allocated for the Logical Name
+ //
+
+ RtlFreeUnicodeString( &TempLogicalNameU );
+
+ //
+ // If necessary, free any string buffer allocated to JoinedPath1U
+ //
+
+ if (JoinedPath1U != NULL) {
+
+ RtlFreeUnicodeString( JoinedPath1U );
+ }
+
+GetNamesFinish:
+
+ return Status;
+}
+
+
+BOOLEAN
+LsapDbIsLocked()
+
+/*++
+
+Routine Description:
+
+ Check if LSA Database is locked.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if LSA database is locked, else false.
+
+--*/
+
+{
+ return (BOOLEAN)(LsapDbState.DbLock.LockCount != -1L);
+}
+
+
+NTSTATUS
+LsarQuerySecurityObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PLSAPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaQuerySecurityObject API returns security information assigned
+ to an LSA Database object.
+
+ Based on the caller's access rights and privileges, this procedure will
+ return a security descriptor containing any or all of the object's owner
+ ID, group ID, discretionary ACL or system ACL. To read the owner ID,
+ group ID, or the discretionary ACL, the caller must be granted
+ READ_CONTROL access to the object. To read the system ACL, the caller must
+ have SeSecurityPrivilege privilege.
+
+ This API is modelled after the NtQuerySecurityObject() system service.
+
+Arguments:
+
+ ObjectHandle - A handle to an existing object in the LSA Database.
+
+ SecurityInformation - Supplies a value describing which pieces of
+ security information are being queried. The values that may be
+ specified are the same as those defined in the NtSetSecurityObject()
+ API section.
+
+ SecurityDescriptor - Receives a pointer to a buffer containing the
+ requested security information. This information is returned in
+ the form of a Self-Relative Security Descriptor.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
+--*/
+
+{
+ NTSTATUS
+ Status,
+ IgnoreStatus;
+
+ LSAP_DB_HANDLE
+ InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+
+ ACCESS_MASK
+ RequiredAccess = 0;
+
+ BOOLEAN
+ Present,
+ IgnoreBoolean;
+
+ LSAP_DB_ATTRIBUTE
+ Attribute;
+
+ PLSAPR_SR_SECURITY_DESCRIPTOR
+ RpcSD = NULL;
+
+ SECURITY_DESCRIPTOR
+ *SD,
+ *ReturnSD;
+
+ ULONG
+ ReturnSDLength;
+
+
+
+ if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // If this is a non-Trusted client, determine the required accesses
+ // for querying the object's Security Descriptor. These accesses
+ // depend on the information being queried.
+ //
+
+ LsapRtlQuerySecurityAccessMask( SecurityInformation, &RequiredAccess );
+
+
+ //
+ // Acquire the Lsa Database lock. Verify that the object handle
+ // is a valid handle (of any type) and is trusted or has
+ // all of the required accesses granted. Reference the container
+ // object handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ ObjectHandle,
+ RequiredAccess,
+ NullObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+
+ //
+ // Read the existing Security Descriptor for the object. This always
+ // exists as the value of the SecDesc attribute of the object.
+ //
+
+ LsapDbInitializeAttribute(
+ &Attribute,
+ &LsapDbNames[ SecDesc ],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ Status = LsapDbReadAttribute( ObjectHandle, &Attribute );
+
+
+
+ if (NT_SUCCESS(Status)) {
+
+ SD = Attribute.AttributeValue;
+ ASSERT( SD != NULL );
+
+
+ //
+ // Elimate components that weren't requested.
+ //
+
+ if ( !(SecurityInformation & OWNER_SECURITY_INFORMATION)) {
+ SD->Owner = NULL;
+ }
+
+ if ( !(SecurityInformation & GROUP_SECURITY_INFORMATION)) {
+ SD->Group = NULL;
+ }
+
+ if ( !(SecurityInformation & DACL_SECURITY_INFORMATION)) {
+ SD->Control &= (~SE_DACL_PRESENT);
+ }
+
+ if ( !(SecurityInformation & SACL_SECURITY_INFORMATION)) {
+ SD->Control &= (~SE_SACL_PRESENT);
+ }
+
+
+ //
+ // Now copy the parts of the security descriptor that we are going to return.
+ //
+
+ ReturnSDLength = 0;
+ ReturnSD = NULL;
+ Status = RtlMakeSelfRelativeSD( (PSECURITY_DESCRIPTOR) SD,
+ (PSECURITY_DESCRIPTOR) ReturnSD,
+ &ReturnSDLength );
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) { // This is the expected case
+
+ ReturnSD = MIDL_user_allocate( ReturnSDLength );
+
+ if (ReturnSD == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ Status = RtlMakeSelfRelativeSD( (PSECURITY_DESCRIPTOR) SD,
+ (PSECURITY_DESCRIPTOR) ReturnSD,
+ &ReturnSDLength );
+ ASSERT( NT_SUCCESS(Status) );
+ }
+ }
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Allocate the first block of returned memory.
+ //
+
+ RpcSD = MIDL_user_allocate( sizeof(LSAPR_SR_SECURITY_DESCRIPTOR) );
+
+ if (RpcSD == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ if (ReturnSD != NULL) {
+ MIDL_user_free( ReturnSD );
+ }
+ } else {
+
+ RpcSD->Length = ReturnSDLength;
+ RpcSD->SecurityDescriptor = (PUCHAR)( (PVOID)ReturnSD );
+ }
+ }
+
+
+ //
+ // free the attribute read from disk
+ //
+
+ MIDL_user_free( SD );
+ }
+
+ IgnoreStatus = LsapDbDereferenceObject(
+ &ObjectHandle,
+ InternalHandle->ObjectTypeId,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+ }
+
+
+ *SecurityDescriptor = RpcSD;
+ return(Status);
+}
+
+
+
+NTSTATUS
+LsarSetSecurityObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN SECURITY_INFORMATION SecurityInformation,
+ IN PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetSecurityObject API takes a well formaed Security Descriptor
+ and assigns specified portions of it to an object. Based on the flags set
+ in the SecurityInformation parameter and the caller's access rights, this
+ procedure will replace any or alll of the security information associated
+ with the object.
+
+ The caller must have WRITE_OWNER access to the object to change the
+ owner or Primary group of the object. The caller must have WRITE_DAC
+ access to the object to change the Discretionary ACL. The caller must
+ have SeSecurityPrivilege to assign a system ACL to an object.
+
+ This API is modelled after the NtSetSecurityObject() system service.
+
+Arguments:
+
+ ObjectHandle - A handle to an existing object in the LSA Database.
+
+ SecurityInformation - Indicates which security information is to be
+ applied to the object. The values that may be specified are the
+ same as those defined in the NtSetSecurityObject() API section.
+ The value(s) to be assigned are passed in the SecurityDescriptor
+ parameter.
+
+ SecurityDescriptor - A pointer to a well formed Self-Relative
+ Security Descriptor.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
+--*/
+
+{
+ NTSTATUS Status;
+ NTSTATUS SecondaryStatus = STATUS_SUCCESS;
+ ACCESS_MASK RequiredAccess = 0;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
+ LSAP_DB_ATTRIBUTE Attribute;
+ PSECURITY_DESCRIPTOR SetSD = NULL;
+ PSECURITY_DESCRIPTOR RetrieveSD = NULL;
+ PSECURITY_DESCRIPTOR ModificationSD = NULL;
+ ULONG RetrieveSDLength;
+ ULONG SetSDLength;
+ BOOLEAN ObjectReferenced = FALSE;
+ HANDLE ClientToken = NULL;
+
+ //
+ // Verify that a Security Descriptor has been passed.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
+
+ goto SetSecurityObjectError;
+ }
+
+ if (!ARGUMENT_PRESENT( SecurityDescriptor->SecurityDescriptor )) {
+
+ goto SetSecurityObjectError;
+ }
+
+ ModificationSD = (PSECURITY_DESCRIPTOR)(SecurityDescriptor->SecurityDescriptor);
+
+ //
+ // If the caller is non-trusted, figure the accesses required
+ // to update the object's Security Descriptor based on the
+ // information being changed.
+ //
+
+ if (!InternalHandle->Trusted) {
+
+ LsapRtlSetSecurityAccessMask( SecurityInformation, &RequiredAccess);
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the object handle
+ // is a valid handle (of any type), and is trusted or has
+ // all of the desired accesses granted. Reference the container
+ // object handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ ObjectHandle,
+ RequiredAccess,
+ NullObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Read the existing Security Descriptor for the object. This always
+ // exists as the value of the SecDesc attribute of the object.
+ //
+
+ LsapDbInitializeAttribute(
+ &Attribute,
+ &LsapDbNames[ SecDesc ],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ Status = LsapDbReadAttribute( ObjectHandle, &Attribute );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+
+ //
+ // Copy the retrieved descriptor into process heap so we can use
+ // RTL routines.
+ //
+
+ RetrieveSD = Attribute.AttributeValue;
+ RetrieveSDLength = Attribute.AttributeValueLength;
+
+ Status = STATUS_INTERNAL_DB_CORRUPTION;
+
+ if (RetrieveSD == NULL) {
+
+ goto SetSecurityObjectError;
+ }
+
+ if (RetrieveSDLength == 0) {
+
+ goto SetSecurityObjectError;
+ }
+
+ SetSD = RtlAllocateHeap( RtlProcessHeap(), 0, RetrieveSDLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (SetSD == NULL) {
+
+ goto SetSecurityObjectError;
+ }
+
+ RtlCopyMemory( SetSD, RetrieveSD, RetrieveSDLength );
+
+ //
+ // If the caller is replacing the owner, then a handle to the impersonation
+ // token is necessary.
+ //
+
+ ClientToken = 0;
+
+ if (SecurityInformation & OWNER_SECURITY_INFORMATION) {
+
+ if (!InternalHandle->Trusted) {
+
+ //
+ // Client is non-trusted. Impersonate the client and
+ // obtain a handle to the impersonation token.
+ //
+
+ Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_QUERY,
+ TRUE, //OpenAsSelf
+ &ClientToken
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_TOKEN) {
+
+ goto SetSecurityObjectError;
+ }
+ }
+
+ //
+ // Stop impersonating the client
+ //
+
+ SecondaryStatus = I_RpcMapWin32Status(RpcRevertToSelf());
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ goto SetSecurityObjectError;
+ }
+
+ } else {
+
+ //
+ // Client is trusted and so is the LSA Process itself. Open the
+ // process token
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY,
+ &ClientToken
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+ }
+ }
+
+ //
+ // Build the replacement security descriptor. This must be done in
+ // process heap to satisfy the needs of the RTL routine.
+ //
+
+ Status = RtlSetSecurityObject(
+ SecurityInformation,
+ ModificationSD,
+ &SetSD,
+ &(LsapDbState.
+ DbObjectTypes[InternalHandle->ObjectTypeId].GenericMapping),
+ ClientToken
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+
+ SetSDLength = RtlLengthSecurityDescriptor( SetSD );
+
+ //
+ // Now replace the existing SD with the updated one.
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ ObjectHandle,
+ &LsapDbNames[SecDesc],
+ SetSD,
+ SetSDLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+
+SetSecurityObjectFinish:
+
+ //
+ // If necessary, close the Client Token.
+ //
+
+ if (ClientToken != 0) {
+
+ SecondaryStatus = NtClose( ClientToken );
+
+ ClientToken = NULL;
+
+ if (!NT_SUCCESS( Status )) {
+
+ goto SetSecurityObjectError;
+ }
+ }
+
+ //
+ // If necessary, free the buffer containing the retrieved SD.
+ //
+
+ if (RetrieveSD != NULL) {
+
+ MIDL_user_free( RetrieveSD );
+ RetrieveSD = NULL;
+ }
+
+ //
+ // If necessary, dereference the object, finish the database
+ // transaction, notify the LSA Database Replicator of the change,
+ // release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &ObjectHandle,
+ InternalHandle->ObjectTypeId,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbChange,
+ Status
+ );
+
+ ObjectReferenced = FALSE;
+ }
+
+ return(Status);
+
+SetSecurityObjectError:
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = SecondaryStatus;
+ }
+
+ goto SetSecurityObjectFinish;
+}
+
+
+NTSTATUS
+LsapDbRebuildCache(
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
+ )
+
+/*++
+
+Routine Description:
+
+ This function rebuilds cached information for a given LSA object type.
+
+Arguments:
+
+ ObjectTypeId - Specifies the Object Type for which the cached information
+ is to be rebuilt.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // If caching is not supporte, just return.
+ //
+
+ if (!LsapDbIsCacheSupported( ObjectTypeId )) {
+
+ goto RebuildCacheFinish;
+ }
+
+ //
+ // Disable caching
+ //
+
+ LsapDbMakeCacheInvalid( ObjectTypeId );
+
+ //
+ // Call the build routine for the specified LSA object Type
+ //
+
+ switch (ObjectTypeId) {
+
+ case PolicyObject:
+
+ Status = LsapDbBuildPolicyCache();
+ break;
+
+ case AccountObject:
+
+ Status = LsapDbBuildAccountCache();
+ break;
+
+ case TrustedDomainObject:
+
+ Status = LsapDbBuildTrustedDomainCache();
+ break;
+
+ case SecretObject:
+
+ Status = LsapDbBuildSecretCache();
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto RebuildCacheError;
+ }
+
+ //
+ // Enable caching.
+ //
+
+ LsapDbMakeCacheValid(ObjectTypeId);
+
+RebuildCacheFinish:
+
+ return(Status);
+
+RebuildCacheError:
+
+ //
+ // Disable caching until the next reboot.
+ //
+
+ LsapDbMakeCacheUnsupported( ObjectTypeId );
+ LsapDbMakeCacheInvalid( ObjectTypeId );
+ goto RebuildCacheFinish;
+}
diff --git a/private/lsa/server/dbp.h b/private/lsa/server/dbp.h
new file mode 100644
index 000000000..5f4cc8241
--- /dev/null
+++ b/private/lsa/server/dbp.h
@@ -0,0 +1,870 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbp.h
+
+Abstract:
+
+ LSA Database Private Functions, Datatypes and Defines
+
+Author:
+
+ Scott Birrell (ScottBi) May 29, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LSADBP_
+#define _LSADBP_
+
+
+
+//
+// LSA revisions
+//
+// NT 1.0 ==> 1.0
+// NT 1.0A ==> 1.1
+//
+
+#define LSAP_DB_REVISION_1_0 0x00010000
+#define LSAP_DB_REVISION_1_1 0x00010001
+#define LSAP_DB_REVISION LSAP_DB_REVISION_1_1
+
+
+//
+// Uncomment the define LSA_SAM_ACCOUNTS_DOMAIN_TEST to enable the
+// code needed for the ctsamdb test program. Recompile dbsamtst.c,
+// dbpolicy.c. rebuild lsasrv.dll and nmake UMTYPE=console UMTEST=ctsamdb.
+//
+// #define LSA_SAM_ACCOUNTS_DOMAIN_TEST
+//
+
+//
+// Prefered Maximum Length of data used for internal enumerations.
+//
+
+#define LSAP_DB_ENUM_DOMAIN_LENGTH ((ULONG) 0x00000100L)
+
+//
+// Write operations are not allowed on Backup controllers (except
+// for trusted clients).
+//
+
+#define LSAP_POLICY_WRITE_OPS (DELETE |\
+ WRITE_OWNER |\
+ WRITE_DAC |\
+ POLICY_TRUST_ADMIN |\
+ POLICY_CREATE_ACCOUNT |\
+ POLICY_CREATE_SECRET |\
+ POLICY_CREATE_PRIVILEGE |\
+ POLICY_SET_DEFAULT_QUOTA_LIMITS |\
+ POLICY_SET_AUDIT_REQUIREMENTS |\
+ POLICY_AUDIT_LOG_ADMIN |\
+ POLICY_SERVER_ADMIN)
+
+#define LSAP_ACCOUNT_WRITE_OPS (DELETE |\
+ WRITE_OWNER |\
+ WRITE_DAC |\
+ ACCOUNT_ADJUST_PRIVILEGES |\
+ ACCOUNT_ADJUST_QUOTAS |\
+ ACCOUNT_ADJUST_SYSTEM_ACCESS)
+
+#define LSAP_TRUSTED_WRITE_OPS (DELETE |\
+ WRITE_OWNER |\
+ WRITE_DAC |\
+ TRUSTED_SET_CONTROLLERS |\
+ TRUSTED_SET_POSIX)
+
+#define LSAP_SECRET_WRITE_OPS (DELETE |\
+ WRITE_OWNER |\
+ WRITE_DAC |\
+ SECRET_SET_VALUE)
+
+//
+// Maximum number of attributes an object can have
+//
+
+#define LSAP_DB_MAX_ATTRIBUTES (0x00000020)
+
+//
+// LSA Absolute Minimum and Maximum Quota Limit values
+//
+// These values represent the endpoints of the range of permitted values
+// which a quota limit may be set via the LsaSetQuotaLimitsForLsa() API
+//
+// FIX, FIX - get real values from Loup
+//
+
+
+#define LSAP_DB_WINNT_PAGED_POOL (0x02000000L)
+#define LSAP_DB_WINNT_NON_PAGED_POOL (0x00100000L)
+#define LSAP_DB_WINNT_MIN_WORKING_SET (0x00010000L)
+#define LSAP_DB_WINNT_MAX_WORKING_SET (0x0f000000L)
+#define LSAP_DB_WINNT_PAGEFILE (0x0f000000L)
+
+#define LSAP_DB_LANMANNT_PAGED_POOL (0x02000000L)
+#define LSAP_DB_LANMANNT_NON_PAGED_POOL (0x00100000L)
+#define LSAP_DB_LANMANNT_MIN_WORKING_SET (0x00010000L)
+#define LSAP_DB_LANMANNT_MAX_WORKING_SET (0x0f000000L)
+#define LSAP_DB_LANMANNT_PAGEFILE (0x0f000000L)
+
+#define LSAP_DB_ABS_MIN_PAGED_POOL (0x00010000L)
+#define LSAP_DB_ABS_MIN_NON_PAGED_POOL (0x00010000L)
+#define LSAP_DB_ABS_MIN_MIN_WORKING_SET (0x00000001L)
+#define LSAP_DB_ABS_MIN_MAX_WORKING_SET (0x00001000L)
+#define LSAP_DB_ABS_MIN_PAGEFILE (0x00001000L)
+
+#define LSAP_DB_ABS_MAX_PAGED_POOL (0xffffffffL)
+#define LSAP_DB_ABS_MAX_NON_PAGED_POOL (0xffffffffL)
+#define LSAP_DB_ABS_MAX_MIN_WORKING_SET (0xffffffffL)
+#define LSAP_DB_ABS_MAX_MAX_WORKING_SET (0xffffffffL)
+#define LSAP_DB_ABS_MAX_PAGEFILE (0xffffffffL)
+
+//
+// NOTES on Logical and Physical Names
+//
+// LogicalName - Unicode String containing the Logical Name of the object.
+// The Logical Name of an object is the name by which it is known
+// to the outside world, e.g, SCOTTBI might be a typical name for
+// a user account object
+// PhysicalName - Unicode String containing the Physical name of the object.
+// This is a name internal to the Lsa Database and is dependent on the
+// implementation. For the current implementation of the LSA Database
+// as a subtree of keys within the Configuration Registry, the
+// PhysicalName is the name of the Registry Key for the object relative
+// to the container object, e.g, ACCOUNTS\SCOTTBI is the Physical Name
+// for the user account object with Logical Name SCOTTBI.
+//
+
+//
+// LSA Database Object Containing Directories
+//
+
+UNICODE_STRING LsapDbContDirs[DummyLastObject];
+
+
+typedef enum _LSAP_DB_CACHE_STATE {
+
+ LsapDbCacheNotSupported = 1,
+ LsapDbCacheInvalid,
+ LsapDbCacheBuilding,
+ LsapDbCacheValid
+
+} LSAP_DB_CACHE_STATE, *PLSAP_DB_CACHE_STATE;
+
+//
+// LSA Database Object Type Structure
+//
+
+typedef struct _LSAP_DB_OBJECT_TYPE {
+
+ GENERIC_MAPPING GenericMapping;
+ ULONG ObjectCount;
+ NTSTATUS ObjectCountError;
+ ULONG MaximumObjectCount;
+ ACCESS_MASK WriteOperations;
+ ACCESS_MASK AliasAdminsAccess;
+ ACCESS_MASK WorldAccess;
+ ACCESS_MASK InvalidMappedAccess;
+ PSID InitialOwnerSid;
+ BOOLEAN ObjectCountLimited;
+ BOOLEAN AccessedBySid;
+ BOOLEAN AccessedByName;
+ LSAP_DB_CACHE_STATE CacheState;
+ PVOID ObjectCache;
+
+} LSAP_DB_OBJECT_TYPE, *PLSAP_DB_OBJECT_TYPE;
+
+//
+// LSA Database Object Name types
+//
+
+typedef enum _LSAP_DB_OBJECT_NAME_TYPE {
+
+ LsapDbObjectPhysicalName = 1,
+ LsapDbObjectLogicalName
+
+} LSAP_DB_OBJECT_NAME_TYPE, *PLSAP_DB_OBJECT_NAME_TYPE;
+
+#define LsapDbMakeCacheUnsupported( ObjectTypeId ) \
+ \
+ { \
+ LsapDbState.DbObjectTypes[ ObjectTypeId ].CacheState = LsapDbCacheInvalid; \
+ }
+
+#define LsapDbMakeCacheInvalid( ObjectTypeId ) \
+ \
+ { \
+ LsapDbState.DbObjectTypes[ ObjectTypeId ].CacheState = LsapDbCacheInvalid; \
+ }
+
+#define LsapDbMakeCacheBuilding( ObjectTypeId ) \
+ \
+ { \
+ LsapDbState.DbObjectTypes[ ObjectTypeId ].CacheState = LsapDbCacheBuilding; \
+ }
+
+
+#define LsapDbMakeCacheValid( ObjectTypeId ) \
+ \
+ { \
+ LsapDbState.DbObjectTypes[ ObjectTypeId ].CacheState = LsapDbCacheValid; \
+ }
+
+#define LsapDbIsCacheValid( ObjectTypeId ) \
+ (LsapDbState.DbObjectTypes[ ObjectTypeId ].CacheState == LsapDbCacheValid)
+
+#define LsapDbIsCacheSupported( ObjectTypeId ) \
+ (LsapDbState.DbObjectTypes[ ObjectTypeId ].CacheState != LsapDbCacheNotSupported)
+
+#define LsapDbIsCacheBuilding( ObjectTypeId ) \
+ (LsapDbState.DbObjectTypes[ ObjectTypeId ].CacheState == LsapDbCacheBuilding)
+
+//
+// LSA Database Local State Information. This structure contains various
+// global variables containing dynamic state information.
+//
+
+typedef struct _LSAP_DB_STATE {
+
+ POLICY_MODIFICATION_INFO PolicyModificationInfo;
+ LARGE_INTEGER ModifiedIdAtLastPromotion;
+ HANDLE DbRootRegKeyHandle; // Lsa Database Root Dir Reg Key Handle
+ PSID PrimaryDomainSid;
+ ULONG SecretObjectCount;
+ ULONG OpenHandleCount;
+ POLICY_LSA_SERVER_ROLE_INFO PolicyLsaServerRoleInfo;
+ BOOLEAN DbServerInitialized;
+ BOOLEAN TransactionOpen;
+ BOOLEAN ReplicatorNotificationEnabled;
+
+ LSAP_DB_OBJECT_TYPE DbObjectTypes[LSAP_DB_OBJECT_TYPE_COUNT];
+
+ RTL_CRITICAL_SECTION DbLock;
+ PRTL_RXACT_CONTEXT RXactContext;
+
+} LSAP_DB_STATE, *PLSAP_DB_STATE;
+
+extern LSAP_DB_STATE LsapDbState;
+
+//
+// LSA Database Private Data. This Data is eligible for replication,
+// unlike the Local State Information above which is meaningful on
+// the local machine only.
+//
+
+typedef struct _LSAP_DB_POLICY_PRIVATE_DATA {
+
+ ULONG NoneDefinedYet;
+
+} LSAP_DB_POLICY_PRIVATE_DATA, *PLSAP_DB_POLICY_PRIVATE_DATA;
+
+PLSAP_CR_CIPHER_KEY LsapDbCipherKey;
+
+
+//
+// Object Enumeration Element Structure
+//
+
+typedef struct _LSAP_DB_ENUMERATION_ELEMENT {
+
+ struct _LSAP_DB_ENUMERATION_ELEMENT *Next;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ PSID Sid;
+ UNICODE_STRING Name;
+
+} LSAP_DB_ENUMERATION_ELEMENT, *PLSAP_DB_ENUMERATION_ELEMENT;
+
+//
+// Handle Table Entry
+//
+
+typedef struct _LSAP_DB_HANDLE_ENTRY {
+
+ BOOLEAN Allocated;
+ HANDLE KeyHandle;
+ ACCESS_MASK GrantedAccess;
+
+} LSAP_DB_HANDLE_ENTRY, *PLSAP_DB_HANDLE_ENTRY;
+
+//
+// Handle Table Handle Block
+//
+
+#define LSAP_DB_MAX_HANDLES_PER_BLOCK 0x00000040L
+
+typedef struct _LSAP_DB_HANDLE_BLOCK {
+
+ struct _LSAP_DB_HANDLE_BLOCK *NextBlock;
+ ULONG FreeCount;
+ LSAP_DB_HANDLE_ENTRY Handles[LSAP_DB_MAX_HANDLES_PER_BLOCK];
+
+} LSAP_DB_HANDLE_BLOCK, *PLSAP_DB_HANDLE_BLOCK;
+
+//
+// Handle Table Header Block
+//
+// One of these structures exists for each Handle Table
+//
+
+typedef struct _LSAP_DB_HANDLE_TABLE {
+
+ BOOLEAN Lock;
+ PLSAP_DB_HANDLE_BLOCK FirstBlock;
+ PLSAP_DB_HANDLE_BLOCK LastBlock;
+
+} LSAP_DB_HANDLE_TABLE, *PLSAP_DB_HANDLE_TABLE;
+
+//
+// Trusted Domain List. This list caches the Trust Information for
+// all Trusted Domains in the Policy Database, and enables lookup
+// operations to locate Trusted Domains by Sid or Name without recourse
+// to the Trusted Domain objects themselves.
+//
+
+typedef struct _LSAP_DB_TRUSTED_DOMAIN_LIST_SECTION {
+
+ LIST_ENTRY Links;
+ ULONG UsedCount;
+ ULONG MaximumCount;
+ PLSAPR_TRUST_INFORMATION Domains;
+
+} LSAP_DB_TRUSTED_DOMAIN_LIST_SECTION, *PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION;
+
+typedef struct _LSAP_DB_TRUSTED_DOMAIN_LIST {
+
+ BOOLEAN Valid;
+ PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION AnchorListSection;
+ LSAP_DB_TRUSTED_DOMAIN_LIST_SECTION DummyAnchorListSection;
+ RTL_RESOURCE Resource;
+
+} LSAP_DB_TRUSTED_DOMAIN_LIST, *PLSAP_DB_TRUSTED_DOMAIN_LIST;
+
+//
+// Account List. This list caches the Account Information for
+// all Account Objects in the Policy database, and enables accounts
+// to queried by Sid without recourse to teh Account objects themselves.
+//
+
+typedef struct _LSAP_DB_ACCOUNT {
+
+ LIST_ENTRY Links;
+ PLSAPR_SID Sid;
+ LSAP_DB_ACCOUNT_TYPE_SPECIFIC_INFO Info;
+
+} LSAP_DB_ACCOUNT, *PLSAP_DB_ACCOUNT;
+
+typedef struct _LSAP_DB_ACCOUNT_LIST {
+
+ LIST_ENTRY Links;
+ ULONG AccountCount;
+
+} LSAP_DB_ACCOUNT_LIST, *PLSAP_DB_ACCOUNT_LIST;
+
+//
+// Cached information for the Policy Object.
+//
+
+typedef struct _LSAP_DB_POLICY_ENTRY {
+
+ ULONG AttributeLength;
+ PLSAPR_POLICY_INFORMATION Attribute;
+
+} LSAP_DB_POLICY_ENTRY, *PLSAP_DB_POLICY_ENTRY;
+
+//
+// Cached policy Object - Initially only Quota Limits is cached.
+//
+
+typedef struct _LSAP_DB_POLICY {
+
+ LSAP_DB_POLICY_ENTRY Info[ PolicyAuditFullQueryInformation + 1];
+
+} LSAP_DB_POLICY, *PLSAP_DB_POLICY;
+
+extern LSAP_DB_POLICY LsapDbPolicy;
+
+NTSTATUS
+LsapDbQueryInformationPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OUT PLSAPR_POLICY_INFORMATION *Buffer
+ );
+
+NTSTATUS
+LsapDbSlowQueryInformationPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OUT PLSAPR_POLICY_INFORMATION *Buffer
+ );
+
+NTSTATUS
+LsapDbBuildPolicyCache(
+ );
+
+NTSTATUS
+LsapDbBuildAccountCache(
+ );
+
+NTSTATUS
+LsapDbBuildTrustedDomainCache(
+ );
+
+NTSTATUS
+LsapDbBuildSecretCache(
+ );
+
+NTSTATUS
+LsapDbRebuildCache(
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
+ );
+
+NTSTATUS
+LsapDbCreateAccount(
+ IN PLSAPR_SID AccountSid,
+ OUT OPTIONAL PLSAP_DB_ACCOUNT *Account
+ );
+
+NTSTATUS
+LsapDbDeleteAccount(
+ IN PLSAPR_SID AccountSid
+ );
+
+NTSTATUS
+LsapDbEnumerateTrustedDomains(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsapDbSlowEnumerateTrustedDomains(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsapDbEnumerateTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsapDbBuildTrustedDomainList(
+ IN OPTIONAL LSA_HANDLE PolicyHandle,
+ OUT OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ );
+
+NTSTATUS
+LsapDbDestroyTrustedDomainList(
+ IN PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ );
+
+
+NTSTATUS
+LsapDbLookupSidTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_SID DomainSid,
+ OUT PLSAPR_TRUST_INFORMATION *TrustInformation
+ );
+
+NTSTATUS
+LsapDbLookupNameTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_UNICODE_STRING DomainName,
+ OUT PLSAPR_TRUST_INFORMATION *TrustInformation
+ );
+
+NTSTATUS
+LsapDbLookupEntryTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION *TrustedDomainListSection,
+ OUT PULONG SectionIndex
+ );
+
+NTSTATUS
+LsapDbTraverseTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION *TrustedDomainListSection,
+ IN OUT PULONG SectionIndex,
+ OUT OPTIONAL PLSAPR_TRUST_INFORMATION *TrustInformation
+ );
+
+NTSTATUS
+LsapDbLocateEntryNumberTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN ULONG EntryNumber,
+ OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_SECTION *TrustedDomainListSection,
+ OUT PULONG SectionIndex,
+ OUT OPTIONAL PLSAPR_TRUST_INFORMATION *TrustInformation
+ );
+
+NTSTATUS
+LsapDbEnumerateTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ );
+
+NTSTATUS
+LsapDbInsertTrustedDomainList(
+ IN ULONG Count,
+ IN PLSAPR_TRUST_INFORMATION Domains
+ );
+
+NTSTATUS
+LsapDbDeleteTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList,
+ IN PLSAPR_TRUST_INFORMATION TrustInformation
+ );
+
+BOOLEAN
+LsapDbIsValidTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ );
+
+NTSTATUS
+LsapDbAcquireReadLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ );
+
+NTSTATUS
+LsapDbAcquireWriteLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ );
+
+VOID
+LsapDbReleaseReadLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ );
+
+VOID
+LsapDbReleaseWriteLockTrustedDomainList(
+ IN OPTIONAL PLSAP_DB_TRUSTED_DOMAIN_LIST TrustedDomainList
+ );
+
+NTSTATUS
+LsapDbOpenPolicyTrustedDomain(
+ IN PLSAPR_TRUST_INFORMATION TrustInformation,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE ControllerPolicyHandle
+ );
+
+NTSTATUS
+LsapDbInitializeHandleTable(
+ );
+
+NTSTATUS
+LsapDbInitializeWellKnownPrivs(
+ );
+
+NTSTATUS
+LsapDbInitializeCipherKey(
+ );
+
+LSAPR_HANDLE
+LsapDbCreateHandle(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ULONG Options
+ );
+
+BOOLEAN LsapDbLookupHandle(
+ IN LSAPR_HANDLE ObjectHandle
+ );
+
+NTSTATUS
+LsapDbCloseHandle(
+ IN LSAPR_HANDLE ObjectHandle
+ );
+
+VOID
+LsapDbFreeHandle(
+ IN LSAPR_HANDLE ObjectHandle
+ );
+
+NTSTATUS
+LsapDbReferencesHandle(
+ IN LSAPR_HANDLE ObjectHandle,
+ OUT PULONG ReferenceCount
+ );
+
+NTSTATUS
+LsapDbMarkDeletedObjectHandles(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN BOOLEAN MarkSelf
+ );
+
+/*++
+
+BOOLEAN
+LsapDbTrustedHandle(
+ IN LSAPR_HANDLE ObjectHandle
+ )
+
+Routine Description:
+
+ This macro function checks if a given handle is Trusted and returns
+ the result.
+
+Arguments:
+
+ ObjectHandle - Valid handle. It is the caller's responsibility
+ to verify that the given handle is valid.
+
+Return Value:
+
+ BOOLEAN - TRUE if handle is Trusted, else FALSE.
+
+--*/
+
+#define LsapDbIsTrustedHandle(ObjectHandle) \
+ (((LSAP_DB_HANDLE) ObjectHandle)->Trusted)
+
+#define LsapDbSidFromHandle(ObjectHandle) \
+ ((PLSAPR_SID)(((LSAP_DB_HANDLE)(ObjectHandle))->Sid))
+
+#define LsapDbObjectTypeIdFromHandle(ObjectHandle) \
+ (((LSAP_DB_HANDLE)(ObjectHandle))->ObjectTypeId)
+
+#define LsapDbRegKeyFromHandle(ObjectHandle) \
+ (((LSAP_DB_HANDLE)(ObjectHandle))->KeyHandle)
+
+#define LsapDbContainerFromHandle(ObjectHandle) \
+ (((LSAP_DB_HANDLE) ObjectHandle)->ContainerHandle)
+
+NTSTATUS
+LsapDbRequestAccessObject(
+ IN OUT LSAPR_HANDLE ObjectHandle,
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbRequestAccessNewObject(
+ IN OUT LSAPR_HANDLE ObjectHandle,
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapDbInitializeObjectTypes();
+
+NTSTATUS
+LsapDbInitializeUnicodeNames();
+
+NTSTATUS
+LsapDbInitializeObjectLinkList();
+
+NTSTATUS
+LsapDbInitializeContainingDirs();
+
+NTSTATUS
+LsapDbInitializeDefaultQuotaLimits();
+
+NTSTATUS
+LsapDbInitializeReplication();
+
+NTSTATUS
+LsapDbInitializeObjectTypes();
+
+NTSTATUS
+LsapDbInitializePrivilegeObject();
+
+NTSTATUS
+LsapDbInitializeLock();
+
+NTSTATUS
+LsapDbOpenRootRegistryKey();
+
+NTSTATUS
+LsapDbInstallLsaDatabase(
+ IN ULONG Pass
+ );
+
+NTSTATUS
+LsapDbInstallPolicyObject(
+ IN ULONG Pass
+ );
+
+NTSTATUS
+LsapDbInstallAccountObjects(
+ VOID
+ );
+
+NTSTATUS
+LsapDbNotifyChangeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType
+ );
+
+NTSTATUS
+LsapDbLogicalToPhysicalNameU(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ OUT PUNICODE_STRING PhysicalNameU
+ );
+
+NTSTATUS
+LsapDbLogicalToPhysicalSubKey(
+ IN LSAPR_HANDLE ObjectHandle,
+ OUT PUNICODE_STRING PhysicalSubKeyNameU,
+ IN PUNICODE_STRING LogicalSubKeyNameU
+ );
+
+NTSTATUS
+LsapDbJoinSubPaths(
+ IN PUNICODE_STRING MajorSubPath,
+ IN PUNICODE_STRING MinorSubPath,
+ OUT PUNICODE_STRING JoinedPath
+ );
+
+VOID
+LsapDbFreePhysicalSubKeyObject(
+ IN PUNICODE_STRING PhysicalSubKeyNameU
+ );
+
+NTSTATUS
+LsapDbGetNamesObject(
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
+ OUT OPTIONAL PUNICODE_STRING LogicalNameU,
+ OUT OPTIONAL PUNICODE_STRING PhysicalNameU
+ );
+
+NTSTATUS
+LsapDbCheckCountObject(
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
+ );
+
+#define LsapDbIncrementCountObject(ObjectTypeId) \
+ { \
+ LsapDbState.DbObjectTypes[ObjectTypeId].ObjectCount++; \
+ }
+
+#define LsapDbDecrementCountObject(ObjectTypeId) \
+ { \
+ LsapDbState.DbObjectTypes[ObjectTypeId].ObjectCount--; \
+ }
+
+NTSTATUS
+LsapDbCreateSDAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation
+ );
+
+
+/*++
+
+Routine Description:
+
+ This macro function determines if a given Object Type Id requires
+ a Sid to be specified in ObjectInformation describing it.
+
+Arguments:
+
+ ObjectTypeId - Object Type Id which must be valid.
+
+Return Values:
+
+ BOOLEAN - TRUE if objects of the given type require a Sid, else FALSE.
+
+#define LsapDbRequiresSidObject(ObjectTypeId) \
+ (LsapDbRequiresSidInfo[ObjectTypeId])
+--*/
+
+/*++
+
+Routine Description:
+
+ This macro function determines if a given Object Type Id requires
+ a name to be specified in ObjectInformation describing it.
+
+Arguments:
+
+ ObjectTypeId - Object Type Id which must be valid.
+
+Return Values:
+
+ BOOLEAN - TRUE if objects of the given type require a name, else FALSE.
+
+#define LsapDbRequiresNameObject(ObjectTypeId) \
+ (LsapDbRequiresNameInfo[ObjectTypeId])
+--*/
+
+NTSTATUS
+LsapDbSetSidNameValue(
+ IN ULONG SidIndex,
+ IN PANSI_STRING AnsiName,
+ IN PANSI_STRING AnsiDomainName,
+ OUT PUNICODE_STRING Name,
+ OUT OPTIONAL PUNICODE_STRING DomainName
+ );
+
+NTSTATUS
+LsapDbQueryValueSecret(
+ IN LSAPR_HANDLE SecretHandle,
+ IN PUNICODE_STRING ValueName,
+ IN OPTIONAL PLSAP_CR_CIPHER_KEY SessionKey,
+ OUT PLSAP_CR_CIPHER_VALUE *CipherValue
+ );
+
+NTSTATUS
+LsapDbGetScopeSecret(
+ IN PLSAPR_UNICODE_STRING SecretName,
+ OUT PBOOLEAN GlobalSecret
+ );
+
+VOID
+LsapDbResetStatesError(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN NTSTATUS PreliminaryStatus,
+ IN ULONG DesiredStatesReset,
+ IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
+ IN ULONG StatesResetAttempted
+ );
+
+VOID
+LsapDbMakeInvalidInformationPolicy(
+ IN ULONG InformationClass
+ );
+
+NTSTATUS
+LsapDbObjectNameFromHandle(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN BOOLEAN MakeCopy,
+ IN LSAP_DB_OBJECT_NAME_TYPE ObjectNameType,
+ OUT PLSAPR_UNICODE_STRING ObjectName
+ );
+
+NTSTATUS
+LsapDbPhysicalNameFromHandle(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN BOOLEAN MakeCopy,
+ OUT PLSAPR_UNICODE_STRING ObjectName
+ );
+
+VOID
+LsapDbMarkTrustedHandle(
+ IN OUT LSAPR_HANDLE ObjectHandle
+ );
+
+VOID
+LsapDbDecrementReferenceCountHandle(
+ IN OUT LSAPR_HANDLE ObjectHandle
+ );
+
+#endif //_LSADBP_
+
diff --git a/private/lsa/server/dbpob.c b/private/lsa/server/dbpob.c
new file mode 100644
index 000000000..2c903a34c
--- /dev/null
+++ b/private/lsa/server/dbpob.c
@@ -0,0 +1,696 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbpob.c
+
+Abstract:
+
+ LSA Database Object Manager - Private Routines
+
+ These routines perform low-level functions private to the LSA Database
+ Object Manager.
+
+Author:
+
+ Scott Birrell (ScottBi) January 8, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+
+NTSTATUS
+LsapDbLogicalToPhysicalSubKey(
+ IN LSAPR_HANDLE ObjectHandle,
+ OUT PUNICODE_STRING PhysicalSubKeyNameU,
+ IN PUNICODE_STRING LogicalSubKeyNameU
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts a Logical Name of a subkey of an open object to
+ the corresponding Physical Name. The Physical Name of a subkey is the
+ hierarchic Registry key name relative to the Registry Key corresponding
+ to the LSA Database root object. It is constructed by extracting the
+ Physical Name of the object from its handle and appending "\" and the
+ given Logical SubKey name.
+
+Arguments:
+
+ ObjectHandle - Handle to open object from an LsapDbOpenObject call.
+
+ PhysicalSubKeyNameU - Pointer to Unicode string that will receive the
+ Physical Name of the subkey.
+
+ LogicalSubKeyNameU - Pointer to Unicode string that contains the
+ Logical name of the subkey.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Not enough system resources to
+ allocate intermediate and final string buffers needed.
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;
+
+ Status = LsapDbJoinSubPaths(
+ &Handle->PhysicalNameU,
+ LogicalSubKeyNameU,
+ PhysicalSubKeyNameU
+ );
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbJoinSubPaths(
+ IN PUNICODE_STRING MajorSubPathU,
+ IN PUNICODE_STRING MinorSubPathU,
+ OUT PUNICODE_STRING JoinedPathU
+ )
+
+/*++
+
+Routine Description:
+
+ This function joins together two parts of a Regsitry SubPath, inserting
+ a "\" as a separator. The Minor SubPath must not begin with a "\".
+ Either or both sub path components may be NULL. Except where both
+ sub path components are NULL, memory is always allocated for the output
+ buffer. This memory must be freed when no longer required by calling
+ RtlFreeUnicodeString() on the output string.
+
+Arguments:
+
+ MajorSubPathU - Pointer to Unicode String containing an absolute or
+ relative subpath.
+
+ MinorSubPathU - Pointer to Unicode String containing a relative
+ subpath.
+
+ JoinedPathU - Pointer to Unicode String that will receive the joined
+ path. Memory will be allocated for the JoinedPath buffer.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Not enough system resources to
+ allocate intermediate and final string buffers needed.
+--*/
+
+{
+ NTSTATUS Status;
+ USHORT JoinedPathLength;
+
+ //
+ // Compute the size needed for the Joined Sub Path string.
+ // The Joined Sub Path has the following form:
+ //
+ // <Major Sub Path> + L"\" + <Minor Sub Path>
+ //
+ // where the "+" operator denotes concatenation.
+ //
+ // If both major and minor sub path are null, then result string
+ // is empty.
+ //
+ // If either major or minor sub path is null, then path separator is
+ // omitted.
+ //
+
+ if (MajorSubPathU == NULL) {
+
+ //
+ // If MinorSubPathU is also NULL, just set the output
+ // buffer size to 0.
+ //
+
+ if (MinorSubPathU == NULL) {
+
+ JoinedPathU->Length = 0;
+ JoinedPathU->Buffer = NULL;
+ return STATUS_SUCCESS;
+ }
+
+ JoinedPathLength = MinorSubPathU->MaximumLength;
+
+ } else if (MinorSubPathU == NULL) {
+
+ JoinedPathLength = MajorSubPathU->MaximumLength;
+
+ } else {
+
+ JoinedPathLength = MajorSubPathU->Length +
+ (USHORT) sizeof( OBJ_NAME_PATH_SEPARATOR ) +
+ MinorSubPathU->Length;
+
+ }
+
+ //
+ // Now allocate buffer for the Joined Sub Path string
+ //
+
+ JoinedPathU->Length = 0;
+ JoinedPathU->MaximumLength = JoinedPathLength;
+ JoinedPathU->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, JoinedPathLength );
+
+ if (JoinedPathU->Buffer == NULL) {
+ goto JoinSubPathError;
+ }
+
+ if (MajorSubPathU != NULL) {
+
+ Status = RtlAppendUnicodeStringToString( JoinedPathU,
+ MajorSubPathU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto JoinSubPathError;
+ }
+
+ }
+
+ if (MinorSubPathU != NULL) {
+
+ if (MajorSubPathU != NULL) {
+
+ Status = RtlAppendUnicodeToString( JoinedPathU,
+ L"\\"
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto JoinSubPathError;
+ }
+ }
+
+ Status = RtlAppendUnicodeStringToString( JoinedPathU,
+ MinorSubPathU
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto JoinSubPathError;
+ }
+
+ }
+
+ return Status;
+
+JoinSubPathError:
+
+ //
+ // If necessary, free the Joined Sub Path string buffer.
+ //
+
+ if (JoinedPathU->Buffer != NULL) {
+
+ RtlFreeHeap( RtlProcessHeap(), 0, JoinedPathU->Buffer );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapDbCreateSDAttributeObject(
+ IN LSAPR_HANDLE ObjectHandle,
+ IN OUT PLSAP_DB_OBJECT_INFORMATION ObjectInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates the initial Security Descriptor Attribute for an LSA
+ Database object. The DACL in this SD is dependent on the object type.
+
+Arguments:
+
+ ObjectHandle - Handle to open object.
+
+ ObjectInformation - Pointer to Object Information structure containing
+ the object's Sid and type. A pointer to the created SD is filled in.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG DaclLength;
+ PACL Dacl;
+ HANDLE LsaProcessHandle = NULL;
+ HANDLE LsaProcessTokenHandle = NULL;
+ PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ ULONG SecurityDescriptorLength;
+ PSECURITY_DESCRIPTOR ContainerDescriptor = NULL;
+ ULONG ContainerDescriptorLength;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ OBJECT_ATTRIBUTES LsaProcessObjectAttributes;
+ SECURITY_DESCRIPTOR CreatorDescriptor;
+ LSAP_DB_HANDLE ContainerHandle;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) ObjectHandle;
+ PTEB CurrentTeb;
+
+ //
+ // We will be creating a Security Descriptor in Self-Relative format.
+ // The information that goes into the SD comes from two sources - the Lsa
+ // Process's token and the information we provide, such as DACL. First,
+ // we need to open the Lsa process to access its token.
+ //
+
+ InitializeObjectAttributes(
+ &LsaProcessObjectAttributes,
+ NULL,
+ 0,
+ NULL,
+ NULL
+ );
+
+ CurrentTeb = NtCurrentTeb();
+
+ Status = NtOpenProcess(
+ &LsaProcessHandle,
+ PROCESS_QUERY_INFORMATION,
+ &LsaProcessObjectAttributes,
+ &CurrentTeb->ClientId
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Now open the Lsa process's token with appropriate access
+ //
+
+ Status = NtOpenProcessToken(
+ LsaProcessHandle,
+ TOKEN_QUERY,
+ &LsaProcessTokenHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Next, we want to specify a DACL to define the access for the
+ // object whose SD is being created.
+ //
+ // Give GENERIC_ALL and, if the object is deletable, DELETE access to
+ // the group DOMAIN_ALIAS_ADMINS.
+ // Give GENERIC_EXECUTE access to WORLD.
+ //
+ // Note that the group ALIAS_ADMINS does NOT require access. This access is not
+ // required because a logon to a member of DOMAIN_ADMIN results in a token
+ // being constructed that has ALIAS_ADMINS added (by an Lsa authentication
+ // filter routine).
+ //
+ // Construct a Security Descriptor that will contain only the DACL
+ // we want and all other fields set to NULL.
+ //
+
+ Status = RtlCreateSecurityDescriptor(
+ &CreatorDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Calculate length of DACL required. It will hold two Access Allowed
+ // ACE's, one for DOMAIN_ALIAS_ADMINS and one for WORLD. The size of the DACL is
+ // the size of the ACL header plus the sizes of the ACE's minus a
+ // redundant ULONG built into the header.
+ //
+
+ DaclLength = sizeof (ACL) - sizeof (ULONG) +
+ sizeof (ACCESS_ALLOWED_ACE ) +
+ RtlLengthSid( LsapAliasAdminsSid ) +
+ sizeof (ACCESS_ALLOWED_ACE ) +
+ RtlLengthSid( LsapWorldSid );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ Dacl = LsapAllocateLsaHeap(DaclLength);
+
+ if (Dacl == NULL) {
+
+ goto CreateSDError;
+ }
+
+ Status = RtlCreateAcl(
+ Dacl,
+ DaclLength,
+ ACL_REVISION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Now add the Access Allowed ACE for the group DOMAIN_ALIAS_ADMINS to the
+ // object's DACL.
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ Dacl,
+ ACL_REVISION,
+ LsapDbState.DbObjectTypes[Handle->ObjectTypeId].AliasAdminsAccess,
+ LsapAliasAdminsSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Now add the Access Allowed ACE for the group WORLD to the
+ // object's DACL.
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ Dacl,
+ ACL_REVISION,
+ LsapDbState.DbObjectTypes[Handle->ObjectTypeId].WorldAccess,
+ LsapWorldSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Set the initial owner of the object
+ //
+
+ Status = RtlSetOwnerSecurityDescriptor(
+ &CreatorDescriptor,
+ LsapDbState.DbObjectTypes[Handle->ObjectTypeId].InitialOwnerSid,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Hook the newly constructed DACL for the LsaDb object into the
+ // Modification Descriptor.
+ //
+
+ Status = RtlSetDaclSecurityDescriptor(
+ &CreatorDescriptor,
+ TRUE,
+ Dacl,
+ FALSE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // If there is a container object, obtain its Security Descriptor so that
+ // we can use it as the basis for our new descriptor. The new
+ // descriptor will be equal to the container descriptor with DACL replaced
+ // by the Modification Descriptor just constructed.
+ //
+ // Reading the container SD takes several steps:
+ //
+ // o Get the length of the Container SD
+ // o Allocate a buffer for the SD
+ // o Read the SD
+ //
+ // Obtain the length of the container object's SD by issuing a read for
+ // the SecDesc subkey of the container object's Registry key, with a
+ // dummy buffer whose size is too small.
+ //
+
+ ContainerHandle = (LSAP_DB_HANDLE) ObjectInformation->ObjectAttributes.RootDirectory;
+
+ if (ContainerHandle != NULL) {
+
+ //
+ // Obtain the length of the container object's SD by issuing a read for
+ // the SecDesc subkey of the container object's Registry key, with a
+ // dummy buffer whose size is too small.
+ //
+
+ ContainerDescriptorLength = 0;
+
+ Status = LsapDbReadAttributeObject(
+ ContainerHandle,
+ &LsapDbNames[SecDesc],
+ NULL,
+ &ContainerDescriptorLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Allocate a buffer from the Lsa Heap for the container object's SD.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ ContainerDescriptor = LsapAllocateLsaHeap( ContainerDescriptorLength );
+
+ if (ContainerDescriptor == NULL) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // Read the container object's SD. It is the value of the SecDesc
+ // subkey.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ ContainerHandle,
+ &LsapDbNames[SecDesc],
+ ContainerDescriptor,
+ &ContainerDescriptorLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+ }
+
+ //
+ // Now we are ready to construct the Self-Relative Security Descriptor.
+ // Information in the SD will be based on that in the LSA Process
+ // Token, except for the DACL we provide in a Security Descriptor.
+ // Note that we pass in the LSA Process Token explicitly because we
+ // are not impersonating a client.
+ //
+
+ Status = RtlNewSecurityObject(
+ ContainerDescriptor,
+ &CreatorDescriptor,
+ &SecurityDescriptor,
+ FALSE,
+ LsaProcessTokenHandle,
+ &(LsapDbState.
+ DbObjectTypes[Handle->ObjectTypeId].GenericMapping)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+ //
+ // 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;
+
+ //
+ // Store the Security Descriptor
+ //
+
+ ObjectInformation->ObjectAttributes.SecurityDescriptor = SecurityDescriptor;
+
+ //
+ // The InitializeObjectAttributes macro stores NULL for the
+ // SecurityQualityOfService field, so we must manually copy that
+ // structure.
+ //
+
+ ObjectInformation->ObjectAttributes.SecurityQualityOfService =
+ &SecurityQualityOfService;
+
+ //
+ // Obtain the length of the newly created SD.
+ //
+
+ SecurityDescriptorLength = RtlLengthSecurityDescriptor(
+ SecurityDescriptor
+ );
+
+ //
+ // Add a Registry transaction to write the Security Descriptor as the
+ // value of the new object's SecDesc subkey.
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ Handle,
+ &(LsapDbNames[SecDesc]),
+ SecurityDescriptor,
+ SecurityDescriptorLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSDError;
+ }
+
+CreateSDFinish:
+
+ //
+ // If necessary, free the memory allocated for the Container Descriptor
+ //
+
+ if (ContainerDescriptor != NULL) {
+
+ LsapFreeLsaHeap( ContainerDescriptor );
+ ContainerDescriptor = NULL;
+ }
+
+ //
+ // If necessary, free the memory allocated for the DACL
+ //
+
+ if (Dacl != NULL) {
+
+ LsapFreeLsaHeap(Dacl);
+ Dacl = NULL;
+ }
+
+ //
+ // Close the Handles to our process and token.
+ //
+
+ if ( LsaProcessHandle != NULL ) {
+ (VOID) NtClose( LsaProcessHandle );
+ }
+
+ if ( LsaProcessTokenHandle != NULL ) {
+ (VOID) NtClose( LsaProcessTokenHandle );
+ }
+
+ return(Status);
+
+CreateSDError:
+
+ //
+ // If necessary, free the memory allocated for SecurityDescriptor.
+ //
+
+ if (SecurityDescriptor != NULL) {
+
+ RtlFreeHeap( RtlProcessHeap(), 0, SecurityDescriptor );
+ SecurityDescriptor = NULL;
+ ObjectInformation->ObjectAttributes.SecurityDescriptor = NULL;
+ }
+
+ goto CreateSDFinish;
+}
+
+
+NTSTATUS
+LsapDbCheckCountObject(
+ IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks if the number of objects of a given type has
+ reached the type-dependent maximum limit (if any). If the limit
+ is reached, a type-dependent error status is returned. Currently,
+ only Secret objects have a limit imposed.
+
+Arguments:
+
+ Handle - Handle to open object.
+
+ ObjectTypeId - Specifies the type of the object.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_TOO_MANY_SECRETS - Too many Secrets
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_DB_OBJECT_TYPE ObjectType;
+
+ ObjectType = &(LsapDbState.DbObjectTypes[ObjectTypeId]);
+
+ //
+ // If there is an Object Count Limit, check that it has not been
+ // reached.
+ //
+
+ if ((ObjectType->ObjectCountLimited) &&
+ (ObjectType->ObjectCount == ObjectType->MaximumObjectCount)) {
+
+ Status = ObjectType->ObjectCountError;
+ }
+
+ return(Status);
+}
+
+
+#define LsapDbIncrementCountObject(ObjectTypeId) \
+ { \
+ LsapDbState.DbObjectTypes[ObjectTypeId].ObjectCount++; \
+ }
+
+
+#define LsapDbDecrementCountObject(ObjectTypeId) \
+ { \
+ LsapDbState.DbObjectTypes[ObjectTypeId].ObjectCount--; \
+ }
diff --git a/private/lsa/server/dbpolicy.c b/private/lsa/server/dbpolicy.c
new file mode 100644
index 000000000..a22cece42
--- /dev/null
+++ b/private/lsa/server/dbpolicy.c
@@ -0,0 +1,4006 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbpolicy.c
+
+Abstract:
+
+ LSA Database - Policy Object Private API Workers
+
+Author:
+
+ Scott Birrell (ScottBi) January 10, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include <lsaisrv.h>
+#include "dbp.h"
+
+#define LSAP_DB_POLICY_MAX_BUFFERS ((ULONG) 0x00000003L)
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Function prototypes private to this module //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+#define LsapDbIsCacheValidPolicyInfoClass( InformationClass ) \
+ (LsapDbPolicy.Info[ InformationClass ].AttributeLength > 0)
+
+NTSTATUS
+LsapDbUpdateInformationPolicy(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OPTIONAL PLSAPR_POLICY_INFORMATION PolicyInformation
+ );
+
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Code //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarOpenPolicy(
+ IN PLSAPR_SERVER_NAME SystemName OPTIONAL,
+ IN PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE PolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server worker dispatch routine for the
+ LsaOpenPolicy API.
+
+ To administer the Local Security Policy of a local or remote system,
+ this API must be called to establish a session with that system's
+ Local Security Authority (LSA) subsystem. This API connects to
+ the LSA of the target system and opens the Policy object
+ of the target system's Local Security Policy database. A handle to
+ the Policy object is returned. This handle must be used
+ on all subsequent API calls to administer the Local Security Policy
+ information for the target system.
+
+Arguments:
+
+ SystemName - Name of the system to be administered. This RPC call
+ only passes in a single character for system name, so it is not
+ passed along to the internal routine.
+
+ ObjectAttributes - Pointer to the set of attributes to use for this
+ connection. The security Quality Of Service information is used and
+ normally should provide Security Identification Class of
+ impersonation. Some operations, however, require Security
+ Impersonation Class of impersonation.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the LSA Subsystem's LSA Database. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target Policy object to determine whether the accesses will be granted or denied.
+
+ PolicyHandle - Receives a handle to be used in future requests.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have access to the target
+ system's LSA Database, or does not have other desired accesses.
+
+--*/
+
+{
+ return(LsapDbOpenPolicy(
+ NULL,
+ ObjectAttributes,
+ DesiredAccess,
+ PolicyHandle,
+ FALSE
+ ));
+}
+
+NTSTATUS
+LsarOpenPolicy2(
+ IN PLSAPR_SERVER_NAME SystemName OPTIONAL,
+ IN PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE PolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server worker dispatch routine for the
+ LsaOpenPolicy API.
+
+ To administer the Local Security Policy of a local or remote system,
+ this API must be called to establish a session with that system's
+ Local Security Authority (LSA) subsystem. This API connects to
+ the LSA of the target system and opens the Policy object
+ of the target system's Local Security Policy database. A handle to
+ the Policy object is returned. This handle must be used
+ on all subsequent API calls to administer the Local Security Policy
+ information for the target system.
+
+ The difference between this call and LsaOpenPolicy is that the entire
+ system name is passed in instead of the first character.
+
+Arguments:
+
+ SystemName - Name of the system to be administered. Administration of
+ the local system is assumed if NULL is specified.
+
+ ObjectAttributes - Pointer to the set of attributes to use for this
+ connection. The security Quality Of Service information is used and
+ normally should provide Security Identification Class of
+ impersonation. Some operations, however, require Security
+ Impersonation Class of impersonation.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the LSA Subsystem's LSA Database. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target Policy object to determine whether the accesses will be granted or denied.
+
+ PolicyHandle - Receives a handle to be used in future requests.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have access to the target
+ system's LSA Database, or does not have other desired accesses.
+
+--*/
+
+{
+ return(LsapDbOpenPolicy(
+ SystemName,
+ ObjectAttributes,
+ DesiredAccess,
+ PolicyHandle,
+ FALSE
+ ));
+}
+
+
+NTSTATUS
+LsaIOpenPolicyTrusted(
+ OUT PLSAPR_HANDLE PolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens a handle to the Policy Object and identifies the
+ caller as a trusted client. Any handles to LSA objects opened via
+ this handle will also be trusted. This function is specifically
+ only for use by clients that form part of the Security Process.
+
+Arguments:
+
+ PolicyHandle - Receives a handle to the Policy Object.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The LSA Policy Database contains
+ an internal inconsistency or invalid value.
+--*/
+
+{
+ return(LsapDbOpenPolicy(
+ NULL,
+ NULL,
+ 0,
+ PolicyHandle,
+ TRUE
+ ));
+}
+
+
+NTSTATUS
+LsapDbOpenPolicy(
+ IN PLSAPR_SERVER_NAME SystemName OPTIONAL,
+ IN OPTIONAL PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE PolicyHandle,
+ IN BOOLEAN TrustedClient
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server worker routine for the LsaOpenPolicy
+ API and the LsaIOpenPolicy private API for trusted clients.
+
+ To administer the Local Security Policy of a local or remote system,
+ this API must be called to establish a session with that system's
+ Local Security Authority (LSA) subsystem. This API connects to
+ the LSA of the target system and opens the Policy object
+ of the target system's Local Security Policy database. A handle to
+ the Policy object is returned. This handle must be used
+ on all subsequent API calls to administer the Local Security Policy
+ information for the target system.
+
+Arguments:
+
+ SystemName - Name of the system to be administered. Administration of
+ the local system is assumed if NULL is specified.
+
+ ObjectAttributes - Pointer to the set of attributes to use for this
+ connection. The security Quality Of Service information is used and
+ normally should provide Security Identification Class of
+ impersonation. Some operations, however, require Security
+ Impersonation Class of impersonation. This parameter MUST
+ be specified for non-Trusted clients (TrustedClient = FALSE)
+ and must not be specified for Trusted Clients.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the LSA Subsystem's LSA Database. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target Policy object to determine whether the accesses will be granted or denied.
+
+ PolicyHandle - Receives a handle to be used in future requests.
+
+ TrustedClient - Indicates whether the client is known to be part of
+ the trusted computer base (TCB). If so (TRUE), no access validation
+ is performed and all requested accesses are granted. If not
+ (FALSE), then the client is impersonated and access validation
+ performed against the SecurityDescriptor on the SERVER object.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The LSA Policy Database contains
+ an internal inconsistency or invalid value.
+
+ STATUS_ACCESS_DENIED - Caller does not have access to the target
+ system's LSA Database, or does not have other desired accesses.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter or combination
+ of parameters,
+
+ STATUS_BACKUP_CONTROLLER - An update access has been requested
+ that is not allowed on Backup Domain Controllers for non-
+ trusted clients.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ ULONG Options;
+ BOOLEAN AcquiredLock = FALSE;
+
+ //
+ // Verify Object Attributes accoring to client trust status.
+ //
+
+ Options = 0;
+
+ if (!TrustedClient) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ //
+ // Client is not trusted. Object Attributes must be specified
+ // and the RootDirectory field must be NULL.
+ //
+
+ if (!ARGUMENT_PRESENT(ObjectAttributes)) {
+
+ goto OpenPolicyError;
+ }
+
+ //
+ // Some update operations have to be allowed on BDC's for non-trusted
+ // clients. These include the creation of local secrets and the
+ // setting of certain information classes. Note that these access
+ // types cover a broad category of operations. In some cases,
+ // only a subset of the possible operations are allowed on BDC's,
+ // for example, POLICY_CREATE_SECRET access will not be disallowed
+ // specifically for BDC's, but non-trusted clients can only create
+ // local secrets. It is simplest to not expressly disallow the
+ // opening of the Policy Object for any access type by a non-trusted
+ // client solely because we're a BDC.
+ //
+
+ Options |= LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK;
+
+ //
+ // Verify that NULL has been specified for the RootDirectory
+ // of ObjectAttributes.
+ //
+
+ if (ObjectAttributes->RootDirectory != NULL) {
+
+ goto OpenPolicyError;
+ }
+
+ //
+ // Copy the supplied ObjectAttributes to the ObjectInformation and
+ // augment them with the name of the Policy Object.
+ //
+
+ ObjectInformation.ObjectAttributes = *((POBJECT_ATTRIBUTES) ObjectAttributes);
+
+ } else {
+
+ //
+ // Trusted Client. All update operations are allowed on BDC's.
+ //
+
+ Options |= LSAP_DB_TRUSTED | LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK;
+
+ InitializeObjectAttributes(
+ &(ObjectInformation.ObjectAttributes),
+ NULL,
+ 0L,
+ NULL,
+ NULL
+ );
+ }
+
+ //
+ // Set the Object Type and Logical Name in the ObjectInformation structure.
+ //
+
+ ObjectInformation.ObjectTypeId = PolicyObject;
+ ObjectInformation.ObjectAttributes.ObjectName = &LsapDbNames[Policy];
+ ObjectInformation.ContainerTypeId = 0;
+ ObjectInformation.Sid = NULL;
+
+ //
+ // Acquire the Lsa Database lock
+ //
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenPolicyError;
+ }
+
+ AcquiredLock = TRUE;
+
+ //
+ // Open the Policy Object. Return the Handle obtained as the
+ // RPC Context Handle.
+ //
+
+ Status = LsapDbOpenObject(
+ &ObjectInformation,
+ DesiredAccess,
+ Options,
+ PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenPolicyError;
+ }
+
+ //
+ // Release the LSA Database lock and return.
+ //
+
+OpenPolicyFinish:
+
+ //
+ // If necessary, release the LSA Database lock.
+ //
+
+ if (AcquiredLock) {
+
+ LsapDbReleaseLock();
+ }
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (*PolicyHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+ return( Status );
+
+OpenPolicyError:
+
+ *PolicyHandle = NULL;
+ goto OpenPolicyFinish;
+
+ //
+ // Usage of the SystemName parameter is hidden within the RPC stub
+ // code, so this parameter will be permanently unreferenced.
+ //
+
+ UNREFERENCED_PARAMETER(SystemName);
+
+}
+
+
+NTSTATUS
+LsaIQueryInformationPolicyTrusted(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_POLICY_INFORMATION *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function is a trusted version of LsarQueryInformationPolicy.
+ Unlike the standard version, no handle is required to the Policy object
+ because an internal handle is used. This routine is available only
+ in the context of the Lsa Process.
+
+Arguments:
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
+ PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAuditFullQueryInformation POLICY_VIEW_AUDIT_INFORMATION
+
+ Buffer - receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to the appropriate LsaIFreeLSAPR_POLICY... routine.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ Result codes returned from LsarQueryInformationPolicy()
+--*/
+
+{
+ return(LsarQueryInformationPolicy(
+ LsapPolicyHandle,
+ InformationClass,
+ Buffer
+ ));
+}
+
+
+NTSTATUS
+LsarQueryInformationPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ OUT PLSAPR_POLICY_INFORMATION *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsarQueryInformationPolicy API.
+
+ The LsaQueryInformationPolicy API obtains information from the Policy
+ object. The caller must have access appropriate to the information
+ being requested (see InformationClass parameter).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
+ PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAuditFullQueryInformation POLICY_VIEW_AUDIT_INFORMATION
+
+ Buffer - receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The Policy Database is possibly
+ corrupt. The returned Policy Information is invalid for
+ the given class.
+--*/
+
+{
+ NTSTATUS Status;
+ ACCESS_MASK DesiredAccess;
+ ULONG ReferenceOptions;
+ ULONG DereferenceOptions;
+ BOOLEAN ObjectReferenced = FALSE;
+
+ //
+ // Validate the Information Class and determine the access required to
+ // query this Policy Information Class.
+ //
+
+ Status = LsapDbVerifyInfoQueryPolicy(
+ PolicyHandle,
+ InformationClass,
+ &DesiredAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoPolicyError;
+ }
+
+ //
+ // If querying the Audit Log Full information, we may need to perform a
+ // test write to the Audit Log to verify that the Log Full status is
+ // up to date. The Audit Log Queue Lock must always be taken
+ // prior to acquiring the LSA Database lock, so take the former lock
+ // here in case we need it.
+ //
+
+ ReferenceOptions = LSAP_DB_ACQUIRE_LOCK;
+ DereferenceOptions = LSAP_DB_RELEASE_LOCK;
+
+ if (InformationClass == PolicyAuditFullQueryInformation) {
+
+ ReferenceOptions |= LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK;
+ DereferenceOptions |= LSAP_DB_RELEASE_LOG_QUEUE_LOCK;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the handle is valid, is
+ // a handle to the Policy object and has the necessary access granted.
+ // Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ DesiredAccess,
+ PolicyObject,
+ ReferenceOptions
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInfoPolicyError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // If caching is enabled for this Information Class, grab the info from the
+ // cache.
+ //
+
+ *Buffer = NULL;
+
+ Status = LsapDbQueryInformationPolicy(
+ LsapPolicyHandle,
+ InformationClass,
+ Buffer
+ );
+
+QueryInfoPolicyFinish:
+
+ //
+ // If necessary, dereference the Policy Object, release the LSA Database lock and
+ // return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ DereferenceOptions,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+QueryInfoPolicyError:
+
+ goto QueryInfoPolicyFinish;
+}
+
+
+NTSTATUS
+LsapDbQueryInformationPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OUT PLSAPR_POLICY_INFORMATION *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the fast LSA server RPC worker routine for the
+ LsarQueryInformationPolicy API. It reads the information
+ from the Policy object cache.
+
+ The LsaQueryInformationPolicy API obtains information from the Policy
+ object. The caller must have access appropriate to the information
+ being requested (see InformationClass parameter).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ NOTE: Currently, this function only allows the
+ PolicyDefaultQuotaInformation information class to be read from
+ the Policy Cache. Other information classes can be added
+ in the future.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
+ PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAuditFullQueryInformation POLICY_VIEW_AUDIT_INFORMATION
+
+ Buffer - Pointer to location that contains either a pointer to the
+ buffer that will be used to return the information. If NULL
+ is contained in this location, a buffer will be allocated via
+ MIDL_user_allocate and a pointer to it returned.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The Policy Database is possibly
+ corrupt. The returned Policy Information is invalid for
+ the given class.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAPR_POLICY_INFORMATION OutputBuffer = NULL;
+ PLSAPR_POLICY_INFORMATION TempBuffer = NULL;
+ ULONG OutputBufferLength;
+ BOOLEAN BufferAllocated = FALSE;
+ PLSAPR_POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo;
+ PLSAPR_POLICY_PRIMARY_DOM_INFO PolicyPrimaryDomainInfo;
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo;
+ PLSAPR_POLICY_PD_ACCOUNT_INFO PolicyPdAccountInfo;
+ PLSAPR_POLICY_REPLICA_SRCE_INFO PolicyReplicaSourceInfo;
+ PVOID SourceBuffers[LSAP_DB_POLICY_MAX_BUFFERS];
+ PVOID DestBuffers[LSAP_DB_POLICY_MAX_BUFFERS];
+ ULONG CopyLength[LSAP_DB_POLICY_MAX_BUFFERS];
+ ULONG NextBufferIndex;
+ ULONG BufferCount = 0;
+ BOOLEAN BufferProvided = FALSE;
+
+ if (*Buffer != NULL) {
+
+ OutputBuffer = *Buffer;
+ BufferProvided = TRUE;
+ }
+
+ //
+ // If caching of the Policy Object is not supported, or has been disabled
+ // until the next system reload, call slow query routine to read the
+ // information from backing storage.
+ //
+
+ if (!LsapDbIsCacheSupported(PolicyObject)) {
+
+ Status = LsapDbSlowQueryInformationPolicy(
+ LsapPolicyHandle,
+ InformationClass,
+ Buffer
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInformationPolicyError;
+ }
+
+ return(Status);
+ }
+
+ //
+ // Caching of the Policy Object is supported, but it may not be
+ // valid. If not valid for any information classes, rebuild the cache.
+ //
+
+ if (!LsapDbIsCacheValid(PolicyObject)) {
+
+ Status = LsapDbBuildPolicyCache();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInformationPolicyError;
+ }
+ }
+
+ //
+ // The cache is now valid but may contain out of date information for
+ // the sepcific Information Class requested. Check for this and rebuild
+ // if necessary.
+ //
+
+ if (!LsapDbIsCacheValidPolicyInfoClass(InformationClass)) {
+
+ Status = LsapDbUpdateInformationPolicy( InformationClass, NULL );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInformationPolicyError;
+ }
+ }
+
+ //
+ // The cache has valid information for this Information Class. Now read
+ // the information desired from the cache. This information consists
+ // of a hierarchic structure with a single root node and zero or more
+ // subnodes. First, read the root node from the cache. We cache its
+ // length too. We need to allocate a buffer if one was not provided.
+ //
+
+ OutputBufferLength = LsapDbPolicy.Info[ InformationClass].AttributeLength;
+
+ if (OutputBuffer == NULL) {
+
+ Status = STATUS_NO_MEMORY;
+
+ if (OutputBufferLength > 0) {
+
+ OutputBuffer = MIDL_user_allocate( OutputBufferLength );
+
+ if (OutputBuffer == NULL) {
+
+ goto QueryInformationPolicyError;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+ BufferAllocated = TRUE;
+ }
+
+ //
+ // Copy data for the root node from the cache
+ //
+
+ RtlCopyMemory(
+ OutputBuffer,
+ LsapDbPolicy.Info[InformationClass].Attribute,
+ OutputBufferLength
+ );
+
+ //
+ // Allocate and copy graph of output (if any)
+ //
+
+ NextBufferIndex = 0;
+
+ switch (InformationClass) {
+
+ case PolicyAuditLogInformation:
+
+ break;
+
+ case PolicyAuditEventsInformation:
+
+ PolicyAuditEventsInfo = (PLSAPR_POLICY_AUDIT_EVENTS_INFO) OutputBuffer;
+
+ //
+ // Setup to copy the Event Auditing Options
+ //
+
+ CopyLength[ NextBufferIndex ] =
+ (PolicyAuditEventsInfo->MaximumAuditEventCount * sizeof(ULONG));
+
+ if (CopyLength[ NextBufferIndex ] > 0) {
+
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ PolicyAuditEventsInfo->EventAuditingOptions =
+ (PPOLICY_AUDIT_EVENT_OPTIONS) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_AUDIT_EVENTS_INFO)
+ LsapDbPolicy.Info[ InformationClass].Attribute)->EventAuditingOptions;
+
+ NextBufferIndex++;
+ }
+
+ Status = STATUS_SUCCESS;
+ break;
+
+ case PolicyPrimaryDomainInformation:
+
+ PolicyPrimaryDomainInfo = (PLSAPR_POLICY_PRIMARY_DOM_INFO) OutputBuffer;
+
+ //
+ // Setup to copy the Unicode Name Buffer
+ //
+
+ CopyLength[ NextBufferIndex ] = (ULONG) PolicyPrimaryDomainInfo->Name.MaximumLength;
+
+ if (CopyLength[ NextBufferIndex ] > 0) {
+
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ PolicyPrimaryDomainInfo->Name.Buffer =
+ (PWSTR) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_PRIMARY_DOM_INFO) LsapDbPolicy.Info[ InformationClass].Attribute)->Name.Buffer;
+
+
+ NextBufferIndex++;
+ }
+
+ //
+ // Setup to copy the Sid (if any). Note that the Primary Domain Sid may
+ // be set to NULL so signify that we have no Primary Domain. This
+ // situation occurs during installation before a Primary Domain
+ // has been specified, or if we're a member of a WorkGroup instead
+ // of a Domain.
+ //
+
+ if (PolicyPrimaryDomainInfo->Sid != NULL) {
+
+ CopyLength[ NextBufferIndex ] = RtlLengthSid(PolicyPrimaryDomainInfo->Sid);
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ PolicyPrimaryDomainInfo->Sid =
+ (PLSAPR_SID) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_PRIMARY_DOM_INFO) LsapDbPolicy.Info[ InformationClass].Attribute)->Sid;
+
+ NextBufferIndex++;
+ }
+
+ Status = STATUS_SUCCESS;
+ break;
+
+ case PolicyAccountDomainInformation:
+
+ PolicyAccountDomainInfo = (PLSAPR_POLICY_ACCOUNT_DOM_INFO) OutputBuffer;
+
+ //
+ // Setup to copy the Unicode Name Buffer
+ //
+
+ CopyLength[ NextBufferIndex ] = (ULONG) PolicyAccountDomainInfo->DomainName.MaximumLength;
+
+ if (CopyLength[ NextBufferIndex ] > 0) {
+
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ PolicyAccountDomainInfo->DomainName.Buffer =
+ (PWSTR) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_ACCOUNT_DOM_INFO) LsapDbPolicy.Info[ InformationClass].Attribute)->DomainName.Buffer;
+
+ NextBufferIndex++;
+ }
+
+ //
+ // Setup to copy the Sid (if any)
+ //
+
+ if (PolicyAccountDomainInfo->DomainSid != NULL) {
+
+ CopyLength[ NextBufferIndex ] = RtlLengthSid(PolicyAccountDomainInfo->DomainSid);
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ PolicyAccountDomainInfo->DomainSid =
+ (PLSAPR_SID) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_ACCOUNT_DOM_INFO) LsapDbPolicy.Info[ InformationClass].Attribute)->DomainSid;
+
+ NextBufferIndex++;
+ }
+
+ Status = STATUS_SUCCESS;
+ break;
+
+ case PolicyPdAccountInformation:
+
+ PolicyPdAccountInfo = (PLSAPR_POLICY_PD_ACCOUNT_INFO) OutputBuffer;
+
+ //
+ // Setup to copy the Unicode Name Buffer
+ //
+
+ CopyLength[ NextBufferIndex ] = (ULONG) PolicyPdAccountInfo->Name.MaximumLength;
+
+ if (CopyLength[ NextBufferIndex ] > 0) {
+
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ PolicyPdAccountInfo->Name.Buffer =
+ (PWSTR) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_PD_ACCOUNT_INFO) LsapDbPolicy.Info[ InformationClass].Attribute)->Name.Buffer;
+
+ NextBufferIndex++;
+ }
+
+ Status = STATUS_SUCCESS;
+ break;
+
+ case PolicyLsaServerRoleInformation:
+
+ break;
+
+ case PolicyReplicaSourceInformation:
+
+ PolicyReplicaSourceInfo = (PLSAPR_POLICY_REPLICA_SRCE_INFO) OutputBuffer;
+
+ //
+ // Setup to copy the Unicode Name Buffer
+ //
+
+ CopyLength[ NextBufferIndex ] = (ULONG) PolicyReplicaSourceInfo->ReplicaSource.MaximumLength;
+
+ if (CopyLength[ NextBufferIndex ] > 0) {
+
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ PolicyReplicaSourceInfo->ReplicaSource.Buffer =
+ (PWSTR) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_PD_ACCOUNT_INFO) LsapDbPolicy.Info[ InformationClass].Attribute)->Name.Buffer;
+
+ NextBufferIndex++;
+ }
+
+ CopyLength[ NextBufferIndex ] = (ULONG) PolicyReplicaSourceInfo->ReplicaAccountName.MaximumLength;
+
+ if (CopyLength[ NextBufferIndex ] > 0) {
+
+ DestBuffers[NextBufferIndex] = MIDL_user_allocate( CopyLength[ NextBufferIndex ] );
+ Status = STATUS_NO_MEMORY;
+
+ if (DestBuffers[NextBufferIndex] == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ PolicyReplicaSourceInfo->ReplicaAccountName.Buffer =
+ (PWSTR) DestBuffers[NextBufferIndex];
+
+ SourceBuffers[NextBufferIndex] =
+ ((PLSAPR_POLICY_PD_ACCOUNT_INFO) LsapDbPolicy.Info[ InformationClass].Attribute)->Name.Buffer;
+
+ NextBufferIndex++;
+ }
+
+ break;
+
+ case PolicyDefaultQuotaInformation:
+
+ break;
+
+ case PolicyModificationInformation:
+
+ break;
+
+ case PolicyAuditFullQueryInformation:
+
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryInformationPolicyError;
+ }
+
+ BufferCount = NextBufferIndex;
+
+ //
+ // Now copy the graph of the output (if any) to the pre-allocated buffers.
+ //
+
+ if (BufferCount > 0) {
+
+ for (NextBufferIndex = 0; NextBufferIndex < BufferCount; NextBufferIndex++) {
+
+ RtlCopyMemory(
+ DestBuffers[NextBufferIndex],
+ SourceBuffers[NextBufferIndex],
+ CopyLength[NextBufferIndex]
+ );
+ }
+ }
+
+ if (!BufferProvided) {
+
+ *Buffer = OutputBuffer;
+ }
+
+QueryInformationPolicyFinish:
+
+ return(Status);
+
+QueryInformationPolicyError:
+
+ if (BufferAllocated) {
+
+ MIDL_user_free(OutputBuffer);
+ OutputBuffer = *Buffer = NULL;
+ BufferAllocated = FALSE;
+ }
+
+ if (BufferCount > 0) {
+
+ for ( NextBufferIndex = 0; NextBufferIndex < BufferCount; NextBufferIndex++ ) {
+
+ MIDL_user_free( DestBuffers[ NextBufferIndex ] );
+ DestBuffers[ NextBufferIndex] = NULL;
+ }
+ }
+
+ goto QueryInformationPolicyFinish;
+}
+
+
+NTSTATUS
+LsapDbSlowQueryInformationPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OUT PLSAPR_POLICY_INFORMATION *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the slow LSA server RPC worker routine for the
+ LsarQueryInformationPolicy API. It actually reads the information
+ from backing storage.
+
+ The LsaQueryInformationPolicy API obtains information from the Policy
+ object. The caller must have access appropriate to the information
+ being requested (see InformationClass parameter).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
+ PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAuditFullQueryInformation POLICY_VIEW_AUDIT_INFORMATION
+
+ Buffer - Pointer to location that contains either a pointer to the
+ buffer that will be used to return the information. If NULL
+ is contained in this location, a buffer will be allocated via
+ MIDL_user_allocate and a pointer to it returned.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INTERNAL_DB_CORRUPTION - The Policy Database is possibly
+ corrupt. The returned Policy Information is invalid for
+ the given class.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PPOLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo;
+ PPOLICY_PD_ACCOUNT_INFO PolicyPdAccountInfo;
+ PPOLICY_REPLICA_SOURCE_INFO PolicyReplicaSourceInfo;
+ PPOLICY_AUDIT_FULL_QUERY_INFO PolicyAuditFullQueryInfo;
+ PLSARM_POLICY_AUDIT_EVENTS_INFO DbPolicyAuditEventsInfo = NULL;
+ ULONG PolicyAuditEventsInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
+ ULONG AttributeCount = 0;
+ ULONG AttributeNumber = 0;
+ PVOID InformationBuffer = NULL;
+ LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_INFO_CLASS_POLICY];
+ PLSAP_DB_ATTRIBUTE NextAttribute;
+ BOOLEAN ObjectReferenced = FALSE;
+ ULONG EventAuditingOptionsSize;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) PolicyHandle;
+ BOOLEAN InfoBufferInAttributeArray = TRUE;
+ BOOLEAN BufferProvided = FALSE;
+
+ if (*Buffer != NULL) {
+
+ BufferProvided = TRUE;
+ }
+
+ //
+ // Compile a list of the attributes that hold the Policy Information of
+ // the specified class.
+ //
+
+ NextAttribute = Attributes;
+
+ switch (InformationClass) {
+
+ case PolicyAuditLogInformation:
+
+ //
+ // Request read of the Audit Log Information.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtLg],
+ NULL,
+ sizeof(POLICY_AUDIT_LOG_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyAuditEventsInformation:
+
+ //
+ // Request read of the Audit Events Information.
+ // intermediate buffer.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtEv],
+ NULL,
+ sizeof(LSARM_POLICY_AUDIT_EVENTS_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyPrimaryDomainInformation:
+
+ //
+ // Request read of the DomainName attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolPrDmN],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Request read of the Sid attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolPrDmS],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyAccountDomainInformation:
+
+ //
+ // Request read of the DomainName attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAcDmN],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Request read of the Sid attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAcDmS],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyPdAccountInformation:
+
+ //
+ // Request read of the DomainName attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolPdAcN],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyLsaServerRoleInformation:
+
+ //
+ // Request Read of the Policy Server Role Info.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolSrvRo],
+ NULL,
+ sizeof (POLICY_LSA_SERVER_ROLE_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyReplicaSourceInformation:
+
+ //
+ // Request read of the Replica Source attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolRepSc],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Request read of the Replica Account Name attribute
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolRepAc],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyDefaultQuotaInformation:
+
+ //
+ // Request read of the Default Quota attribute.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[DefQuota],
+ NULL,
+ sizeof (POLICY_DEFAULT_QUOTA_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyModificationInformation:
+
+ //
+ // Request read of the Policy Modification Information
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolMod],
+ NULL,
+ sizeof (POLICY_MODIFICATION_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ case PolicyAuditFullQueryInformation:
+
+ //
+ // Request read of the Policy Audit Full Information
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtFL],
+ NULL,
+ sizeof (POLICY_AUDIT_FULL_QUERY_INFO),
+ FALSE
+ );
+
+ NextAttribute++;
+ AttributeCount++;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryInformationPolicyError;
+ }
+
+ //
+ //
+ // Read the attributes corresponding to the given Policy Information
+ // Class. Memory will be allocated where required for output
+ // Attribute Value buffers, via MIDL_user_allocate().
+ //
+
+ Status = LsapDbReadAttributesObject(
+ PolicyHandle,
+ Attributes,
+ AttributeCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // Some attributes may not exist because they were never set
+ // or were deleted because they were set to NULL values.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto SlowQueryInformationPolicyError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Now copy the information read to the output. The following flags
+ // are used to control freeing of memory buffers:
+ //
+ // InfoBufferInAttributeArray
+ //
+ // If set to TRUE (the default), the information to be returned to
+ // the caller consists of a single buffer which was read directly
+ // from a single attribute of the Policy object and can be returned
+ // as is to the caller. The information buffer being returned is
+ // therefore referenced by the single Attribute Information block's
+ // AttributeValue field.
+ //
+ // If set to FALSE, the information to be returned to the caller
+ // does not satisfy the above. The information to be returned is
+ // either obtained from a single attribute, but is in a different form
+ // from that read from the Database, or it is complex, consisting
+ // of information read from multiple attributes, hung off a top-level
+ // node. In these cases, the top level information buffer is not
+ // referenced by any member of the Attribute Info Array.
+ //
+ // Attribute->MemoryAllocated
+ //
+ // When an attribute is read via LsapDbReadAttributesObject, this
+ // field is set to TRUE to indicate that memory was allocated via
+ // MIDL_user_allocate() for the AttributeValue. If this memory
+ // buffer is to be returned to the caller (i.e. referenced from
+ // the output structure graph returned), it is set to FALSE so that
+ // the normal success finish part of this routine will not free it.
+ // In this case, the calling server RPC stub will free the memory after
+ // marshalling its contents into the return buffer. If this memory
+ // buffer is not to be returned to the calling RPC server stub (because
+ // the memory is an intermediate buffer), the field is left set to TRUE
+ // so that normal cleanup will free it.
+ //
+
+ NextAttribute = Attributes;
+
+ switch (InformationClass) {
+
+ case PolicyAuditLogInformation:
+
+ InformationBuffer = NextAttribute->AttributeValue;
+
+ //
+ // We can use this buffer as is, so we don't want to free it here.
+ //
+
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ case PolicyAuditEventsInformation:
+
+ //
+ // An intermediate buffer is required, because the Audit Events
+ // read from the database are in a different form from those
+ // returned.
+ //
+
+ DbPolicyAuditEventsInfo = NextAttribute->AttributeValue;
+ InfoBufferInAttributeArray = FALSE;
+
+ //
+ // Allocate Buffer for output in final format. This differs
+ // slightly from the self-relative format in which this
+ // Information Class is stored.
+ //
+
+ PolicyAuditEventsInfo = MIDL_user_allocate(sizeof (POLICY_AUDIT_EVENTS_INFO));
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PolicyAuditEventsInfo == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Need to allocate memory via MIDL_user_allocate for the
+ // EventAuditingOptions pointer since we are not using
+ // the midl allocate_all_nodes feature for the LSAPR_POLICY_INFORMATION
+ // structure graph on this server side.
+ //
+
+ EventAuditingOptionsSize = LSARM_AUDIT_EVENT_OPTIONS_SIZE;
+
+ PolicyAuditEventsInfo->EventAuditingOptions =
+ MIDL_user_allocate(EventAuditingOptionsSize);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PolicyAuditEventsInfo->EventAuditingOptions == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // If Policy Audit Event Info was read from the LSA Database, copy
+ // its fields to output, otherwise return values with Auditing
+ // Disabled and no Auditing set foro any Event Types.
+ //
+
+ if (DbPolicyAuditEventsInfo != NULL) {
+
+ PolicyAuditEventsInfo->AuditingMode =
+ DbPolicyAuditEventsInfo->AuditingMode;
+ PolicyAuditEventsInfo->MaximumAuditEventCount =
+ DbPolicyAuditEventsInfo->MaximumAuditEventCount;
+
+ //
+ // Copy over the Event Auditing Options
+ //
+
+ RtlCopyMemory(
+ PolicyAuditEventsInfo->EventAuditingOptions,
+ DbPolicyAuditEventsInfo->EventAuditingOptions,
+ LSARM_AUDIT_EVENT_OPTIONS_SIZE \
+ );
+
+ } else {
+
+ PolicyAuditEventsInfo->AuditingMode = FALSE;
+ PolicyAuditEventsInfo->MaximumAuditEventCount =
+ POLICY_AUDIT_EVENT_TYPE_COUNT;
+
+ RtlZeroMemory(
+ PolicyAuditEventsInfo->EventAuditingOptions,
+ LSARM_AUDIT_EVENT_OPTIONS_SIZE \
+ );
+ }
+
+ InformationBuffer = PolicyAuditEventsInfo;
+ break;
+
+ case PolicyPrimaryDomainInformation:
+
+ //
+ // Allocate memory for output buffer top-level structure.
+ //
+
+ InfoBufferInAttributeArray = FALSE;
+ PolicyPrimaryDomainInfo =
+ MIDL_user_allocate(sizeof (POLICY_PRIMARY_DOMAIN_INFO));
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PolicyPrimaryDomainInfo == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Copy the Unicode Name field to the output. Original buffer will
+ // be freed in Finish section.
+ //
+
+ Status = LsapDbCopyUnicodeAttribute(
+ &PolicyPrimaryDomainInfo->Name,
+ NextAttribute,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ NextAttribute++;
+
+ //
+ // Copy the Sid to the output. We can use this buffer as is
+ // since it was allocated via MIDL_user_allocate, so just copy the
+ // buffer pointer and clear the MemoryAllocated flag in the
+ // attribute information so we don't free it in the Finish section.
+ //
+
+ PolicyPrimaryDomainInfo->Sid = (PSID) NextAttribute->AttributeValue;
+
+ InformationBuffer = PolicyPrimaryDomainInfo;
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ case PolicyAccountDomainInformation:
+
+ //
+ // Allocate memory for output buffer top-level structure.
+ //
+
+ InfoBufferInAttributeArray = FALSE;
+ PolicyAccountDomainInfo =
+ MIDL_user_allocate(sizeof(POLICY_ACCOUNT_DOMAIN_INFO));
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PolicyAccountDomainInfo == NULL) {
+
+ break;
+ }
+
+ //
+ // Copy the Unicode DomainName field to the output. Original buffer will
+ // be freed in Finish section.
+ //
+
+ Status = LsapDbCopyUnicodeAttribute(
+ &PolicyAccountDomainInfo->DomainName,
+ NextAttribute,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ NextAttribute++;
+
+ //
+ // Copy the Sid to the output. We can use this buffer as is
+ // since it was allocated via MIDL_user_allocate, so just copy the
+ // buffer pointer and clear the MemoryAllocated flag in the
+ // attribute information so we don't free it in the Finish section.
+ //
+
+ PolicyAccountDomainInfo->DomainSid = (PSID) NextAttribute->AttributeValue;
+
+ InformationBuffer = PolicyAccountDomainInfo;
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ case PolicyPdAccountInformation:
+
+ //
+ // Allocate memory for output buffer top-level structure.
+ //
+
+ InfoBufferInAttributeArray = FALSE;
+ PolicyPdAccountInfo = MIDL_user_allocate(sizeof(POLICY_PD_ACCOUNT_INFO));
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PolicyPdAccountInfo == NULL) {
+
+ break;
+ }
+
+ //
+ // Copy the Unicode Name field to the output. Original buffer will
+ // be freed in Finish section.
+ //
+
+ Status = LsapDbCopyUnicodeAttribute(
+ &PolicyPdAccountInfo->Name,
+ NextAttribute,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ InformationBuffer = PolicyPdAccountInfo;
+ break;
+
+ case PolicyLsaServerRoleInformation:
+
+ //
+ // We can use this buffer as is, so we don't want to free it here.
+ //
+
+ InformationBuffer = NextAttribute->AttributeValue;
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ case PolicyReplicaSourceInformation:
+
+ //
+ // Allocate memory for output buffer top-level structure.
+ //
+
+ InfoBufferInAttributeArray = FALSE;
+ PolicyReplicaSourceInfo =
+ MIDL_user_allocate(sizeof(POLICY_REPLICA_SOURCE_INFO));
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PolicyReplicaSourceInfo == NULL) {
+
+ break;
+ }
+
+ //
+ // Copy the Unicode ReplicaSource field to the output. Original buffer will
+ // be freed in Finish section.
+ //
+
+ Status = LsapDbCopyUnicodeAttribute(
+ &PolicyReplicaSourceInfo->ReplicaSource,
+ NextAttribute,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ NextAttribute++;
+
+ //
+ // Copy the Unicode ReplicaAccountName field to the output. Original buffer will
+ // be freed in Finish section.
+ //
+
+ Status = LsapDbCopyUnicodeAttribute(
+ &PolicyReplicaSourceInfo->ReplicaAccountName,
+ NextAttribute,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ InformationBuffer = PolicyReplicaSourceInfo;
+ break;
+
+ case PolicyDefaultQuotaInformation:
+
+ //
+ // We can use this buffer as is, so we don't want to free it here.
+ //
+
+ InformationBuffer = NextAttribute->AttributeValue;
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ case PolicyModificationInformation:
+
+ //
+ // We can use this buffer as is, so we don't want to free it here.
+ //
+
+ InformationBuffer = NextAttribute->AttributeValue;
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ case PolicyAuditFullSetInformation:
+
+ //
+ // We can use this buffer as is, so we don't want to free it here.
+ //
+
+ InformationBuffer = NextAttribute->AttributeValue;
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ case PolicyAuditFullQueryInformation:
+
+// PolicyAuditFullQueryInfo = (PPOLICY_AUDIT_FULL_QUERY_INFO)
+// InformationBuffer;
+
+ //
+ // We can use this buffer, so we don't want to free it here.
+ //
+
+ InformationBuffer = NextAttribute->AttributeValue;
+
+ PolicyAuditFullQueryInfo = (PPOLICY_AUDIT_FULL_QUERY_INFO)
+ InformationBuffer;
+
+ //
+ // Lie about the result. Fix this for product 2
+ //
+
+ PolicyAuditFullQueryInfo->LogIsFull = FALSE;
+
+// //
+// // We need to ensure that the Audit Log Full status returned
+// // in this buffer is up-to-date. Check the returned status and
+// // if it indicates "audit log full", check if the log is really full
+// // by trying to write to it.
+// //
+//
+// if (PolicyAuditFullQueryInfo->LogIsFull) {
+//
+// Status = LsapAdtQueryAuditLogFullInfo(
+// LsapDbHandle,
+// LSAP_ADT_LOG_FULL_UPDATE,
+// PolicyAuditFullQueryInfo
+// );
+// }
+
+ NextAttribute->MemoryAllocated = FALSE;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SlowQueryInformationPolicyError;
+ }
+
+ //
+ // Verify that the returned Policy Information is valid. If not,
+ // the Policy Database is corrupt.
+ //
+
+ Status = STATUS_INTERNAL_DB_CORRUPTION;
+
+ if (!LsapDbValidInfoPolicy(InformationClass, InformationBuffer)) {
+
+ goto SlowQueryInformationPolicyError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // If the caller provided a buffer, return information there.
+ //
+
+ if (BufferProvided) {
+
+ RtlCopyMemory(
+ *Buffer,
+ InformationBuffer,
+ LsapDbPolicy.Info[ InformationClass ].AttributeLength
+ );
+
+ MIDL_user_free( InformationBuffer );
+ InformationBuffer = NULL;
+
+ } else {
+
+ *Buffer = InformationBuffer;
+ }
+
+SlowQueryInformationPolicyFinish:
+
+ //
+ // Free any unwanted buffers that were allocated by
+ // LsapDbReadAttributesObject() and that are not being returned to the
+ // caller server stub. The server stub will free the buffers that we
+ // do return after copying them to the return RPC transmit buffer.
+ //
+
+ for (NextAttribute = Attributes, AttributeNumber = 0;
+ AttributeNumber < AttributeCount;
+ NextAttribute++, AttributeNumber++) {
+
+ //
+ // If buffer holding attribute is marked as allocated, it is
+ // to be freed here.
+ //
+
+ if (NextAttribute->MemoryAllocated) {
+
+ if (NextAttribute->AttributeValue != NULL) {
+
+ MIDL_user_free(NextAttribute->AttributeValue);
+ NextAttribute->AttributeValue = NULL;
+ NextAttribute->MemoryAllocated = FALSE;
+ }
+ }
+ }
+
+ return(Status);
+
+SlowQueryInformationPolicyError:
+
+ //
+ // If necessary, free the memory allocated for the output buffer.
+ // We only do this free if the buffer is not referenced by the
+ // attribute array, since all buffers so referenced will be freed
+ // here or in the Finish section.
+ //
+
+ if ((InformationBuffer != NULL) && !InfoBufferInAttributeArray) {
+
+ MIDL_user_free(InformationBuffer);
+ InformationBuffer = NULL;
+ }
+
+ //
+ // Free the buffers referenced by the attributes array that will not be
+ // freed by the Finish section of this routine.
+ //
+
+ for (NextAttribute = Attributes, AttributeNumber = 0;
+ AttributeNumber < AttributeCount;
+ NextAttribute++, AttributeNumber++) {
+
+ //
+ // If buffer holding attribute is marked as normally not to be freed,
+ // will not get freed by the Finish section so it must be freed here.
+ //
+
+ if (!NextAttribute->MemoryAllocated) {
+
+ if (NextAttribute->AttributeValue != NULL) {
+
+ MIDL_user_free(NextAttribute->AttributeValue);
+ NextAttribute->AttributeValue = NULL;
+ NextAttribute->MemoryAllocated = FALSE;
+ }
+
+ NextAttribute->MemoryAllocated = FALSE;
+ }
+ }
+
+ goto SlowQueryInformationPolicyFinish;
+}
+
+
+NTSTATUS
+LsarSetInformationPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_POLICY_INFORMATION PolicyInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaSetInformationPolicy API.
+
+ The LsaSetInformationPolicy API modifies information in the Policy Object.
+ The caller must have access appropriate to the information to be changed
+ in the Policy Object, see the InformationClass parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationClass - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ PolicyAuditLogInformation POLICY_AUDIT_LOG_ADMIN
+ PolicyAuditEventsInformation POLICY_SET_AUDIT_REQUIREMENTS
+ PolicyPrimaryDomainInformation POLICY_TRUST_ADMIN
+ PolicyAccountDomainInformation POLICY_TRUST_ADMIN
+ PolicyPdAccountInformation Not settable by this API
+ PolicyLsaServerRoleInformation POLICY_SERVER_ADMIN
+ PolicyReplicaSourceInformation POLICY_SERVER_ADMIN
+ PolicyDefaultQuotaInformation POLICY_SET_DEFAULT_QUOTA_LIMITS
+ PolicyAuditFullSetInformation POLICY_AUDIT_LOG_ADMIN
+
+ Buffer - Points to a structure containing the information appropriate
+ to the information type specified by the InformationClass parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+
+{
+ NTSTATUS Status, SavedStatus;
+ ACCESS_MASK DesiredAccess;
+
+ PPOLICY_AUDIT_EVENTS_INFO ModifyPolicyAuditEventsInfo;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo;
+ PPOLICY_LSA_SERVER_ROLE_INFO PolicyLsaServerRoleInfo;
+ PPOLICY_REPLICA_SOURCE_INFO PolicyReplicaSourceInfo;
+ PPOLICY_DEFAULT_QUOTA_INFO PolicyDefaultQuotaInfo;
+ PPOLICY_MODIFICATION_INFO PolicyModificationInfo;
+ PPOLICY_AUDIT_FULL_SET_INFO PolicyAuditFullSetInfo;
+
+ LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_INFO_CLASS_POLICY];
+ PLSAP_DB_ATTRIBUTE NextAttribute;
+ ULONG AttributeCount = 0;
+ ULONG AttributeNumber;
+ POLICY_AUDIT_EVENT_TYPE AuditEventType;
+ PLSARM_POLICY_AUDIT_EVENTS_INFO PreviousPolicyAuditEventsInfo = NULL;
+ PLSARM_POLICY_AUDIT_EVENTS_INFO UpdatedPolicyAuditEventsInfo = NULL;
+ ULONG UpdatedPolicyAuditEventsInfoLength;
+ ULONG UpdatedMaximumAuditEventCount;
+ ULONG ModifyMaximumAuditEventCount;
+ PPOLICY_AUDIT_EVENT_OPTIONS UpdatedEventAuditingOptions;
+ PPOLICY_AUDIT_EVENT_OPTIONS ModifyEventAuditingOptions;
+ PPOLICY_AUDIT_EVENT_OPTIONS PreviousEventAuditingOptions;
+ ULONG PolicyAuditEventsInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
+ ULONG PreviousPolicyAuditEventsInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
+ PUNICODE_STRING DomainName = NULL;
+ PUNICODE_STRING AccountName = NULL;
+ PUNICODE_STRING ReplicaSource = NULL;
+ BOOLEAN ObjectReferenced = FALSE;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) PolicyHandle;
+ ULONG NewMaximumAuditEventCount = 0;
+ BOOLEAN PreviousAuditEventsInfoExists;
+ ULONG ReferenceOptions, DereferenceOptions;
+ POLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo;
+ LARGE_INTEGER ModifiedIdAtLastPromotion;
+
+ PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
+ BOOLEAN BooleanStatus;
+ BOOLEAN WerePolicyChangesAuditedBefore = FALSE;
+
+ //
+ // Validate the Information Class and Policy Information provided and
+ // if valid, return the mask of accesses required to update this
+ // class of policy information.
+ //
+
+ Status = LsapDbVerifyInfoSetPolicy(
+ PolicyHandle,
+ InformationClass,
+ PolicyInformation,
+ &DesiredAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInformationPolicyError;
+ }
+
+ //
+ // Set the options for referencing the Policy Object. We need to
+ // acquire the LSA Database Lock and start a transaction. Normally,
+ // the object reference routine will disallow updates to a Backup
+ // Domain Controller from non-trusted clients, but a non-trusted
+ // client is allowed to revert the server role to Primary Controller.
+ // A special flag is used to allow this operation to go through.
+ //
+
+ ReferenceOptions = LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION;
+ DereferenceOptions = LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION;
+
+ if ((InformationClass == PolicyLsaServerRoleInformation) ||
+ (InformationClass == PolicyAccountDomainInformation) ||
+ (InformationClass == PolicyPrimaryDomainInformation)) {
+
+ ReferenceOptions |= LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK;
+ }
+
+ //
+ // If we are setting the Policy Audit Log Information, we may need
+ // the Audit Log Queue Lock.
+ //
+
+ if (InformationClass == PolicyAuditLogInformation) {
+
+ ReferenceOptions |= (LSAP_DB_ACQUIRE_LOG_QUEUE_LOCK | LSAP_DB_OMIT_REPLICATOR_NOTIFICATION);
+ DereferenceOptions |= LSAP_DB_RELEASE_LOG_QUEUE_LOCK;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the handle is
+ // valid, is a handle to the Policy Object and has the necessary accesses
+ // granted. Reference the handle and start an Lsa Database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ DesiredAccess,
+ PolicyObject,
+ ReferenceOptions
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInformationPolicyError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Update the specified information in the Policy Object.
+ //
+
+ NextAttribute = Attributes;
+
+ switch (InformationClass) {
+
+ case PolicyAuditLogInformation:
+
+ {
+ //
+ // This operation is no longer supported. Return an
+ // error to anyone who tries except trusted clients who
+ // are just blindly replcate the entire database.
+ //
+
+ LSAP_DB_HANDLE InternalHandle = PolicyHandle;
+
+ if (!InternalHandle->Trusted) {
+
+ Status = STATUS_NOT_IMPLEMENTED;
+ }
+ }
+
+ break;
+
+ case PolicyAuditEventsInformation:
+
+ //
+ // NOTE: If turning Auditing back on and the LogIsFull state is set,
+ // it is automatically cleared. When the Audit Log becomes full and
+ // the system is shutdown, auditing is automatically disabled upon
+ // reboot. The user is forced to log on as Administrator to clear
+ // the log, or modify its size/retention. When Auditing is turned
+ // back on, the LogIsFull state is automatically cleared.
+ //
+ // IMPORTANT: To allow new Audit Event Types to be added to the
+ // system in successive versions, this code caters for the
+ // following situations:
+ //
+ // (1) The LSA Database is older than the present system and
+ // contains information for fewer Audit Event Types than
+ // currently supported.
+ //
+ // (2) The client code is older than the present system and
+ // specifies fewer Audit Event Types than currently supported.
+ // In this case, the newer options will be left unchanged.
+ //
+ // In all cases, the updated information written to the LSA Database
+ // and transmitted to the Reference Monitor within the Nt Executive
+ // contains Event Auditing Options for every Audit Event Type
+ // currently supported.
+ //
+ // Additionally, this code caters for old LSA Databases that have
+ // no default Audit Event Information. This is a very temporary
+ // situation, since installation now initializes this information.
+ //
+ // If no information has been provided or there is more information
+ // than the current Audit Event Info structure holds, return an error.
+ // Note that the caller is allowed to specify infomration for
+ // the firt n Audit Events, where n is less than the current
+ // number the system supports. This allows new events to be
+ // added without the need to change calling code.
+ //
+
+ WerePolicyChangesAuditedBefore = LsapAdtAuditingPolicyChanges();
+
+ ModifyPolicyAuditEventsInfo = (PPOLICY_AUDIT_EVENTS_INFO) PolicyInformation;
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (ModifyPolicyAuditEventsInfo == NULL) {
+
+ break;
+ }
+
+ UpdatedMaximumAuditEventCount = POLICY_AUDIT_EVENT_TYPE_COUNT;
+
+ //
+ //
+ // The following check is disabled so that replication will work when
+ // reading from a PDC with pre-Build 354 Auditing Event Information
+ // in which there were 12 categories.
+ //
+ //
+ // if (ModifyPolicyAuditEventsInfo->MaximumAuditEventCount >
+ // UpdatedMaximumAuditEventCount) {
+ //
+ // break;
+ // }
+ //
+
+ if (ModifyPolicyAuditEventsInfo->MaximumAuditEventCount == 0) {
+
+ break;
+ }
+
+ //
+ // Read Existing Audit Events. Specify NULL for the buffer pointer
+ // so that the read routine will allocate the buffer for us.
+ // Specify 0 for the length, because we don't know what it is.
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtEv],
+ NULL,
+ 0,
+ FALSE
+ );
+
+ Status = LsapDbReadAttribute( PolicyHandle, NextAttribute );
+
+ if (NT_SUCCESS(Status)) {
+
+ PreviousPolicyAuditEventsInfo = NextAttribute->AttributeValue;
+
+ Status = STATUS_INTERNAL_DB_CORRUPTION;
+
+ if (PreviousPolicyAuditEventsInfo == NULL) {
+
+ break;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ PreviousPolicyAuditEventsInfoLength = NextAttribute->AttributeValueLength;
+ PreviousAuditEventsInfoExists = TRUE;
+
+ } else {
+
+ //
+ // Unable to read existing Audit Event Options. If this is
+ // because there is no Audit Event Information in an old
+ // Database, then, temorarily, we will proceed as if Auditing
+ // and all Options were disabled. NOTE: This situation will NOT
+ // occur in the finished product.
+ //
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ break;
+ }
+
+ PreviousAuditEventsInfoExists = FALSE;
+ }
+
+ //
+ // Setup a buffer to hold the updated Audit Event Information.
+ // We try to use the existing buffer if possible.
+ //
+
+ if (PreviousAuditEventsInfoExists &&
+ ModifyPolicyAuditEventsInfo->MaximumAuditEventCount <=
+ PreviousPolicyAuditEventsInfo->MaximumAuditEventCount) {
+
+ //
+ // There is an existing Audit Event Info buffer and it is
+ // large enough so update it in situ.
+ //
+
+ UpdatedPolicyAuditEventsInfo = PreviousPolicyAuditEventsInfo;
+ UpdatedPolicyAuditEventsInfoLength = PreviousPolicyAuditEventsInfoLength;
+ UpdatedEventAuditingOptions = PreviousPolicyAuditEventsInfo->EventAuditingOptions;
+
+ } else {
+
+ //
+ // There is either no existing buffer or it is not large
+ // enough. We need to allocate a new one for the updated
+ // information. This will store the number of Audit Event
+ // Types that the system currently supports.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ UpdatedPolicyAuditEventsInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
+ UpdatedPolicyAuditEventsInfo = MIDL_user_allocate( UpdatedPolicyAuditEventsInfoLength );
+
+ if (UpdatedPolicyAuditEventsInfo == 0) {
+
+ goto SetInformationPolicyError;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ UpdatedPolicyAuditEventsInfo->AuditingMode = FALSE;
+ UpdatedEventAuditingOptions =
+ UpdatedPolicyAuditEventsInfo->EventAuditingOptions;
+
+ for ( AuditEventType=0 ;
+ AuditEventType < (POLICY_AUDIT_EVENT_TYPE) UpdatedMaximumAuditEventCount ;
+ AuditEventType++ ) {
+
+ UpdatedEventAuditingOptions[ AuditEventType ] = 0;
+ }
+
+ if (!PreviousAuditEventsInfoExists) {
+
+ PreviousPolicyAuditEventsInfo = UpdatedPolicyAuditEventsInfo;
+ }
+ }
+
+ //
+ // Construct the updated Audit Event Info, applying the Modification
+ // information provided. Note that for an old database we may be
+ // writing more info back than we read.
+ //
+
+ PreviousEventAuditingOptions = PreviousPolicyAuditEventsInfo->EventAuditingOptions;
+ ModifyMaximumAuditEventCount = ModifyPolicyAuditEventsInfo->MaximumAuditEventCount;
+ ModifyEventAuditingOptions = ModifyPolicyAuditEventsInfo->EventAuditingOptions;
+
+ for ( AuditEventType = 0;
+ AuditEventType < (POLICY_AUDIT_EVENT_TYPE) ModifyMaximumAuditEventCount;
+ AuditEventType++ ) {
+
+ if ( ModifyEventAuditingOptions[ AuditEventType ] & POLICY_AUDIT_EVENT_NONE ) {
+
+ //
+ // Clear all existing flags for this Audit Event Type.
+ //
+
+ UpdatedEventAuditingOptions[ AuditEventType ] = 0;
+
+ }
+
+ //
+ // Apply new flags.
+ //
+
+ UpdatedEventAuditingOptions[ AuditEventType ] |=
+ (ModifyEventAuditingOptions[ AuditEventType ] &
+ ( POLICY_AUDIT_EVENT_MASK & ~POLICY_AUDIT_EVENT_NONE));
+ }
+
+ //
+ // Update the Auditing Mode as specified. Set the Maximum Audit Event
+ // Count.
+ //
+
+ UpdatedPolicyAuditEventsInfo->AuditingMode = ModifyPolicyAuditEventsInfo->AuditingMode;
+ UpdatedPolicyAuditEventsInfo->MaximumAuditEventCount = UpdatedMaximumAuditEventCount;
+
+ //
+ // Update global variables that keep track of whether or not we
+ // are auditing logon events
+ //
+
+ LsapAdtAuditingLogon( UpdatedPolicyAuditEventsInfo );
+
+ //
+ // Ship the new Auditing Options to the Kernel.
+ //
+
+
+ Status = LsapCallRm(
+ RmAuditSetCommand,
+ (PVOID) UpdatedPolicyAuditEventsInfo,
+ sizeof(LSARM_POLICY_AUDIT_EVENTS_INFO),
+ NULL,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ RtlCopyMemory(
+ &LsapAdtEventsInformation,
+ UpdatedPolicyAuditEventsInfo,
+ sizeof(LSARM_POLICY_AUDIT_EVENTS_INFO)
+ );
+
+ //
+ // Update Audit Event Category Info held by SAM
+ //
+
+ PolicyAuditEventsInfo.AuditingMode = UpdatedPolicyAuditEventsInfo->AuditingMode;
+ PolicyAuditEventsInfo.MaximumAuditEventCount = POLICY_AUDIT_EVENT_TYPE_COUNT;
+ PolicyAuditEventsInfo.EventAuditingOptions =
+ UpdatedPolicyAuditEventsInfo->EventAuditingOptions;
+
+ Status = SamISetAuditingInformation(&PolicyAuditEventsInfo);
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ //
+ // Setup attribute info for writing the updated Audit Event Info
+ // to the LSA Database (PolAdtEv attribute of the Policy Object).
+ //
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtEv],
+ UpdatedPolicyAuditEventsInfo,
+ UpdatedPolicyAuditEventsInfoLength,
+ FALSE
+ );
+
+ AttributeCount++;
+
+ //
+ // If we are turning Auditing back on and the Audit Log Full flag is
+ // set, clear this flag.
+ //
+
+ if ((UpdatedPolicyAuditEventsInfo->AuditingMode == TRUE) &&
+ (PreviousPolicyAuditEventsInfo->AuditingMode == FALSE) &&
+ LsapAdtLogFullInformation.LogIsFull) {
+
+ LsapAdtLogFullInformation.LogIsFull = FALSE;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtFL],
+ &LsapAdtLogFullInformation,
+ sizeof (POLICY_AUDIT_FULL_QUERY_INFO),
+ FALSE
+ );
+
+ AttributeCount++;
+
+ //
+ // Invalidate the information in the Policy Cache for this information
+ // class
+ //
+
+ LsapDbMakeInvalidInformationPolicy( PolicyAuditFullQueryInformation );
+ }
+
+ break;
+
+ case PolicyPrimaryDomainInformation:
+
+ PolicyPrimaryDomainInfo = (PPOLICY_PRIMARY_DOMAIN_INFO) PolicyInformation;
+
+ //
+ // Construct the Domain name attribute info
+ //
+
+ Status = LsapDbMakeUnicodeAttribute(
+ &PolicyPrimaryDomainInfo->Name,
+ &LsapDbNames[PolPrDmN],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ DomainName = NextAttribute->AttributeName;
+ NextAttribute++;
+ AttributeCount++;
+
+ //
+ // Construct the Sid attribute info
+ //
+
+ Status = LsapDbMakeSidAttribute(
+ PolicyPrimaryDomainInfo->Sid,
+ &LsapDbNames[PolPrDmS],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AttributeCount++;
+ break;
+
+ case PolicyAccountDomainInformation:
+
+ PolicyAccountDomainInfo = (PPOLICY_ACCOUNT_DOMAIN_INFO) PolicyInformation;
+
+ //
+ // Construct the Domain name attribute info
+ //
+
+ Status = LsapDbMakeUnicodeAttribute(
+ &PolicyAccountDomainInfo->DomainName,
+ &LsapDbNames[PolAcDmN],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ DomainName = NextAttribute->AttributeName;
+ AttributeCount++;
+ NextAttribute++;
+
+ //
+ // Construct the Sid attribute info
+ //
+
+ Status = LsapDbMakeSidAttribute(
+ PolicyAccountDomainInfo->DomainSid,
+ &LsapDbNames[PolAcDmS],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AttributeCount++;
+ break;
+
+ case PolicyPdAccountInformation:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ case PolicyLsaServerRoleInformation:
+
+ PolicyLsaServerRoleInfo = (PPOLICY_LSA_SERVER_ROLE_INFO) PolicyInformation;
+
+ //
+ // Setup is allowed to call this routine before the product
+ // type has been set, but not before RtlGetNtProductType()
+ // is functional (and contains the correct value).
+ // In this situation, initialize the global variable here.
+ //
+
+ if (!LsapDbIsServerInitialized()) {
+ BooleanStatus = RtlGetNtProductType(&LsapProductType);
+ ASSERT(BooleanStatus);
+ }
+
+ //
+ // Make sure the role is valid (primary or backup)
+ //
+
+ if ((PolicyLsaServerRoleInfo->LsaServerRole != PolicyServerRoleBackup) &&
+ (PolicyLsaServerRoleInfo->LsaServerRole != PolicyServerRolePrimary)) {
+
+ Status = STATUS_INVALID_DOMAIN_ROLE;
+ break;
+ }
+ //
+ // Only NTAS systems can be demoted.
+ //
+
+ if (LsapProductType != NtProductLanManNt) {
+
+ if ( (PolicyLsaServerRoleInfo->LsaServerRole
+ == PolicyServerRoleBackup) //Trying to demote
+ ) {
+
+ Status = STATUS_INVALID_DOMAIN_ROLE;
+ break;
+
+ }
+ }
+
+ //
+ // See if we are a backup being promoted to
+ // primary. If so, then we have special ModifiedId
+ // increment requirements.
+ //
+
+ if ( (LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole
+ == PolicyServerRoleBackup) //Currently Backup
+ &&
+ (PolicyLsaServerRoleInfo->LsaServerRole
+ == PolicyServerRolePrimary) //Changing to Primary
+ ) {
+
+ LARGE_INTEGER PromotionIncrement = LSA_PROMOTION_INCREMENT;
+
+ ModifiedIdAtLastPromotion.QuadPart =
+ LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart +
+ PromotionIncrement.QuadPart +
+ 1;
+
+ DereferenceOptions |= LSAP_DB_PROMOTION_INCREMENT;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolPromot],
+ &ModifiedIdAtLastPromotion,
+ sizeof (LARGE_INTEGER),
+ FALSE
+ );
+
+ LsapDbState.ModifiedIdAtLastPromotion = ModifiedIdAtLastPromotion;
+
+ NextAttribute++;
+ AttributeCount++;
+ }
+
+ LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole = PolicyLsaServerRoleInfo->LsaServerRole;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolSrvRo],
+ &PolicyLsaServerRoleInfo->LsaServerRole,
+ sizeof (POLICY_LSA_SERVER_ROLE_INFO),
+ FALSE
+ );
+
+ AttributeCount++;
+ break;
+
+ case PolicyReplicaSourceInformation:
+
+ PolicyReplicaSourceInfo = (PPOLICY_REPLICA_SOURCE_INFO) PolicyInformation;
+
+ //
+ // Construct the Replica Source Name attribute info
+ //
+
+ Status = LsapDbMakeUnicodeAttribute(
+ &PolicyReplicaSourceInfo->ReplicaSource,
+ &LsapDbNames[PolRepSc],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ ReplicaSource = NextAttribute->AttributeName;
+ AttributeCount++;
+ NextAttribute++;
+
+ //
+ // Construct the Replica Account Name attribute info
+ //
+
+ Status = LsapDbMakeUnicodeAttribute(
+ &PolicyReplicaSourceInfo->ReplicaAccountName,
+ &LsapDbNames[PolRepAc],
+ NextAttribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ AccountName = NextAttribute->AttributeName;
+ AttributeCount++;
+ break;
+
+ case PolicyDefaultQuotaInformation:
+
+ PolicyDefaultQuotaInfo = (PPOLICY_DEFAULT_QUOTA_INFO) PolicyInformation;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[DefQuota],
+ &PolicyDefaultQuotaInfo->QuotaLimits,
+ sizeof (POLICY_DEFAULT_QUOTA_INFO),
+ FALSE
+ );
+
+ AttributeCount++;
+ break;
+
+ case PolicyModificationInformation:
+
+ PolicyModificationInfo = (PPOLICY_MODIFICATION_INFO) PolicyInformation;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolMod],
+ PolicyModificationInfo,
+ sizeof (POLICY_MODIFICATION_INFO),
+ FALSE
+ );
+
+ AttributeCount++;
+ break;
+
+ case PolicyAuditFullSetInformation:
+
+ PolicyAuditFullSetInfo = (PPOLICY_AUDIT_FULL_SET_INFO) PolicyInformation;
+ LsapAdtLogFullInformation.ShutDownOnFull =
+ PolicyAuditFullSetInfo->ShutDownOnFull;
+
+ LsapDbInitializeAttribute(
+ NextAttribute,
+ &LsapDbNames[PolAdtFL],
+ &LsapAdtLogFullInformation,
+ sizeof (POLICY_AUDIT_FULL_QUERY_INFO),
+ FALSE
+ );
+
+ AttributeCount++;
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetInformationPolicyError;
+ }
+
+ //
+ // Update the Policy Object attributes
+
+ Status = LsapDbWriteAttributesObject(
+ PolicyHandle,
+ Attributes,
+ AttributeCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DereferenceOptions |= LSAP_DB_REBUILD_CACHE;
+
+ goto SetInformationPolicyError;
+ }
+
+ //
+ // Generate an audit if necessary.
+ //
+
+ if (LsapAdtAuditingPolicyChanges() || WerePolicyChangesAuditedBefore) {
+
+ SavedStatus = Status;
+
+ Status = LsapAdtGenerateLsaAuditEvent(
+ PolicyHandle,
+ SE_CATEGID_POLICY_CHANGE,
+ SE_AUDITID_POLICY_CHANGE,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ &LsapAdtEventsInformation
+ );
+
+ //
+ // Ignore failure status from auditing.
+ //
+
+ Status = SavedStatus;
+
+ //
+ // Free the memory containing the TrustInformation.
+ //
+
+ LsaIFree_LSAPR_TRUST_INFORMATION ( TrustInformation );
+ TrustInformation = NULL;
+ }
+
+SetInformationPolicyFinish:
+
+ //
+ // If the Policy Server Role has been changed, notify the LSA
+ // Database Replicator of the new role.
+ //
+ // Tell netlogon about the new role before the LsapDbDereferenceObject
+ // to ensure netlogon writes the promotion to the change log.
+ //
+
+ if (NT_SUCCESS(Status) && InformationClass == PolicyLsaServerRoleInformation) {
+
+ Status = LsapDbNotifyRoleChangePolicy(
+ PolicyLsaServerRoleInfo->LsaServerRole
+ );
+ }
+
+ //
+ // If necessary, finish any Lsa Database transaction, notify the
+ // LSA Database Replicator of the change, dereference the Policy Object,
+ // release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ //
+ // Invalidate the information in the Policy Cache for this information
+ // class
+ //
+
+ LsapDbMakeInvalidInformationPolicy( InformationClass );
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ DereferenceOptions,
+ SecurityDbChange,
+ Status
+ );
+
+ ObjectReferenced = FALSE;
+ }
+
+ //
+ // Free memory allocated by this routine for attribute buffers.
+ // These have MemoryAllocated = TRUE in their attribute information.
+ // Leave alone buffers allocated by calling RPC stub.
+ //
+
+ for( NextAttribute = Attributes, AttributeNumber = 0;
+ AttributeNumber < AttributeCount;
+ NextAttribute++, AttributeNumber++) {
+
+ if (NextAttribute->MemoryAllocated) {
+
+ if (NextAttribute->AttributeValue != NULL) {
+
+ MIDL_user_free(NextAttribute->AttributeValue);
+ NextAttribute->MemoryAllocated = FALSE;
+ NextAttribute->AttributeValue = NULL;
+ }
+ }
+ }
+
+ //
+ // If necessary, free memory allocated for the Previous Audit Event
+ // Information. Only do this if it is not the same as the
+ // Updated Audit Event Information pointer.
+ //
+
+ if (PreviousPolicyAuditEventsInfo != NULL) {
+
+ if (PreviousPolicyAuditEventsInfo != UpdatedPolicyAuditEventsInfo) {
+
+ MIDL_user_free( PreviousPolicyAuditEventsInfo );
+ PreviousPolicyAuditEventsInfo = NULL;
+ }
+ }
+
+ return(Status);
+
+SetInformationPolicyError:
+
+ goto SetInformationPolicyFinish;
+}
+
+
+VOID
+LsapDbMakeInvalidInformationPolicy(
+ IN ULONG InformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This function frees and invalidates the information held for a specific
+ Information Class in the Policy Object cache. The general cache state
+ remains unchanged.
+
+Arguments:
+
+ InformationClass - Specifies the Information Class whose information is to be
+ discarded.
+
+Return Values:
+
+--*/
+
+{
+ //
+ // If the Policy Cache is invalid, just return.
+ //
+
+ if (!LsapDbIsCacheValid(PolicyObject)) {
+
+ return;
+ }
+
+ //
+ //
+ // If PolicyAuditFullSetInformation is specified, free
+ // PolicyAuditFullQueryInformation
+ //
+
+ if (InformationClass == PolicyAuditFullSetInformation) {
+
+ InformationClass = PolicyAuditFullQueryInformation;
+ }
+
+ //
+ // If the information in the cache for this Information Class is invalid,
+ // just return
+ //
+
+ if (!LsapDbIsCacheValidPolicyInfoClass( InformationClass )) {
+
+ return;
+ }
+
+ if (LsapDbPolicy.Info[InformationClass].AttributeLength != 0) {
+
+ LsaIFree_LSAPR_POLICY_INFORMATION (
+ InformationClass,
+ (PLSAPR_POLICY_INFORMATION) LsapDbPolicy.Info[ InformationClass ].Attribute
+ );
+
+ LsapDbPolicy.Info[InformationClass].Attribute = NULL;
+ LsapDbPolicy.Info[InformationClass].AttributeLength = 0;
+ }
+
+ return;
+}
+
+
+NTSTATUS
+LsapDbVerifyInfoQueryPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ OUT PACCESS_MASK RequiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function validates a Policy Information Class. If valid, a mask
+ of the accesses required to set the Policy Information of the class is
+ returned.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsapDbOpenPolicy call. The handle
+ may be trusted.
+
+ InformationClass - Specifies a Policy Information Class.
+
+ RequiredAccess - Points to variable that will receive a mask of the
+ accesses required to query the given class of Policy Information.
+ If an error is returned, this value is cleared to 0.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The Policy Information Class provided is
+ valid and the information provided is consistent with this
+ class.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter:
+
+ Information Class is invalid
+ Policy Information not valid for the class
+--*/
+
+{
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) PolicyHandle;
+
+ if (LsapDbValidInfoPolicy( InformationClass, NULL)) {
+
+ //
+ // PolicyAuditFullSetInformation information class is not
+ // allowed for a Query. PolicyAuditFullQueryInformation must
+ // be used instead.
+ //
+
+ if ( InformationClass == PolicyAuditFullSetInformation ) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Non-trusted callers are not allowed to query the
+ // PolicyModificationInformation information class.
+ //
+
+ if (!InternalHandle->Trusted) {
+
+ if (InformationClass == PolicyModificationInformation) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ *RequiredAccess = LsapDbRequiredAccessQueryPolicy[InformationClass];
+ return(STATUS_SUCCESS);
+ }
+
+ return(STATUS_INVALID_PARAMETER);
+}
+
+
+NTSTATUS
+LsapDbVerifyInfoSetPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_POLICY_INFORMATION PolicyInformation,
+ OUT PACCESS_MASK RequiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This function validates a Policy Information Class and verifies
+ that the provided Policy Information is valid for the class.
+ If valid, a mask of the accesses required to set the Policy
+ Information of the class is returned.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsapDbOpenPolicy call. The handle
+ may be trusted.
+
+ InformationClass - Specifies a Policy Information Class.
+
+ PolicyInformation - Points to Policy Information to be set.
+
+ RequiredAccess - Points to variable that will receive a mask of the
+ accesses required to set the given class of Policy Information.
+ If an error is returned, this value is cleared to 0.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The Policy Information Class provided is
+ valid and the information provided is consistent with this
+ class.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter:
+
+ Information Class is invalid
+ Information Class is invalid for non-trusted clients
+ Policy Information not valid for the class
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) PolicyHandle;
+
+ //
+ // Verify that the information class is valid and that the Policy
+ // Information provided is valid for the class.
+ //
+
+ if (LsapDbValidInfoPolicy( InformationClass, PolicyInformation)) {
+
+ //
+ // PolicyAuditFullQueryInformation information class is not
+ // allowed for a Set. PolicyAuditFullSetInformation must
+ // be used instead.
+ //
+
+ if ( InformationClass == PolicyAuditFullQueryInformation ) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Non-trusted callers are not allowed to set information for
+ // the following classes.
+ //
+ // PolicyPdAccountInformation
+ // PolicyModificationInformation
+ // PolicyAuditFullQueryInformation
+ //
+
+ if (!InternalHandle->Trusted) {
+
+ if ((InformationClass == PolicyPdAccountInformation) ||
+ (InformationClass == PolicyModificationInformation) ||
+ (InformationClass == PolicyAuditFullQueryInformation)) {
+
+#ifdef LSA_SAM_ACCOUNTS_DOMAIN_TEST
+
+ if (InformationClass == PolicyPdAccountInformation) {
+
+ Status = LsapDbTestLoadSamAccountsDomain(
+ (PUNICODE_STRING) PolicyInformation
+ );
+ }
+
+#endif // LSA_SAM_ACCOUNTS_DOMAIN_TEST
+ return(STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ *RequiredAccess = LsapDbRequiredAccessSetPolicy[InformationClass];
+ return(STATUS_SUCCESS);
+ }
+
+ Status = STATUS_INVALID_PARAMETER;
+ return(Status);
+}
+
+
+BOOLEAN
+LsapDbValidInfoPolicy(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OPTIONAL PLSAPR_POLICY_INFORMATION PolicyInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function validates a Policy Information Class and optionally verifies
+ that provided Policy Information is valid for the class.
+
+Arguments:
+
+ InformationClass - Specifies a Policy Information Class.
+
+ PolicyInformation - Optionally points to Policy Information. If
+ NULL is specified, no Policy Information checking takes place.
+
+Return Values:
+
+ BOOLEAN - TRUE if the Policy information class provided is
+ valid, else FALSE.
+--*/
+
+{
+ BOOLEAN BooleanStatus = TRUE;
+ PPOLICY_AUDIT_LOG_INFO PolicyAuditLogInfo;
+ PPOLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo;
+ PPOLICY_PD_ACCOUNT_INFO PolicyPdAccountInfo;
+ PPOLICY_LSA_SERVER_ROLE_INFO PolicyLsaServerRoleInfo;
+ PPOLICY_REPLICA_SOURCE_INFO PolicyReplicaSourceInfo;
+ PPOLICY_DEFAULT_QUOTA_INFO PolicyDefaultQuotaInfo;
+ PPOLICY_MODIFICATION_INFO PolicyModificationInfo;
+ PPOLICY_AUDIT_FULL_SET_INFO PolicyAuditFullSetInfo;
+ PPOLICY_AUDIT_FULL_QUERY_INFO PolicyAuditFullQueryInfo;
+ POLICY_AUDIT_EVENT_TYPE AuditEventType;
+ ULONG MaximumAuditEventCount;
+ PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions;
+
+ //
+ // Validate the Information Class
+ //
+
+ if ((InformationClass >= PolicyAuditLogInformation) &&
+ (InformationClass <= PolicyAuditFullQueryInformation)) {
+
+ if (PolicyInformation == NULL) {
+
+ return(TRUE);
+ }
+
+ switch (InformationClass) {
+
+ case PolicyAuditLogInformation:
+
+ PolicyAuditLogInfo = (PPOLICY_AUDIT_LOG_INFO) PolicyInformation;
+
+ break;
+
+ case PolicyAuditEventsInformation:
+
+ PolicyAuditEventsInfo = (PPOLICY_AUDIT_EVENTS_INFO) PolicyInformation;
+
+ if (PolicyAuditEventsInfo == NULL) {
+
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ MaximumAuditEventCount = PolicyAuditEventsInfo->MaximumAuditEventCount;
+
+ if (MaximumAuditEventCount > POLICY_AUDIT_EVENT_TYPE_COUNT) {
+
+ //
+ // The following is a temporary hack to allow replication
+ // to work with the PDC has the PolicyAuditEventInfomation
+ // as it was prior to build 354 with 12 Audit Event Categories.
+ // We simply truncate it.
+ //
+
+ MaximumAuditEventCount = POLICY_AUDIT_EVENT_TYPE_COUNT;
+ }
+
+ if (MaximumAuditEventCount == 0) {
+
+ BooleanStatus = FALSE;
+ break;
+ }
+
+ EventAuditingOptions = PolicyAuditEventsInfo->EventAuditingOptions;
+
+ try {
+
+ //
+ // Verify that the Event Auditing Options are meaningful.
+ //
+
+ for (AuditEventType = 0;
+ AuditEventType < (POLICY_AUDIT_EVENT_TYPE) MaximumAuditEventCount;
+ AuditEventType++) {
+
+ if (EventAuditingOptions[ AuditEventType ] !=
+
+ (EventAuditingOptions[ AuditEventType ] & POLICY_AUDIT_EVENT_MASK )) {
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ BooleanStatus = FALSE;
+ }
+
+ break;
+
+ case PolicyPrimaryDomainInformation:
+
+ PolicyPrimaryDomainInfo = (PPOLICY_PRIMARY_DOMAIN_INFO) PolicyInformation;
+
+ break;
+
+ case PolicyAccountDomainInformation:
+
+ PolicyAccountDomainInfo = (PPOLICY_ACCOUNT_DOMAIN_INFO) PolicyInformation;
+
+ break;
+
+ case PolicyPdAccountInformation:
+
+ PolicyPdAccountInfo = (PPOLICY_PD_ACCOUNT_INFO) PolicyInformation;
+
+ break;
+
+ case PolicyLsaServerRoleInformation:
+
+ PolicyLsaServerRoleInfo = (PPOLICY_LSA_SERVER_ROLE_INFO) PolicyInformation;
+
+ break;
+
+ case PolicyReplicaSourceInformation:
+
+ PolicyReplicaSourceInfo = (PPOLICY_REPLICA_SOURCE_INFO) PolicyInformation;
+
+ break;
+
+ case PolicyDefaultQuotaInformation:
+
+ PolicyDefaultQuotaInfo = (PPOLICY_DEFAULT_QUOTA_INFO) PolicyInformation;
+ break;
+
+ case PolicyModificationInformation:
+
+ PolicyModificationInfo = (PPOLICY_MODIFICATION_INFO) PolicyInformation;
+ break;
+
+ case PolicyAuditFullSetInformation:
+
+ PolicyAuditFullSetInfo = (PPOLICY_AUDIT_FULL_SET_INFO) PolicyInformation;
+ break;
+
+ case PolicyAuditFullQueryInformation:
+
+ PolicyAuditFullQueryInfo = (PPOLICY_AUDIT_FULL_QUERY_INFO) PolicyInformation;
+ break;
+
+ default:
+
+ BooleanStatus = FALSE;
+ break;
+ }
+ }
+
+ return(BooleanStatus);
+}
+
+
+NTSTATUS
+LsaIGetSerialNumberPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ OUT PLARGE_INTEGER ModifiedCount,
+ OUT PLARGE_INTEGER CreationTime
+ )
+
+/*++
+
+Routine Description:
+
+ Thin wrapper to LsaIGetSerialNumberPolicy2().
+ See that function for descriptions.
+
+
+--*/
+
+{
+ LARGE_INTEGER
+ Ignore1;
+
+ return( LsaIGetSerialNumberPolicy2( PolicyHandle,
+ ModifiedCount,
+ &Ignore1,
+ CreationTime
+ ) );
+
+}
+
+
+NTSTATUS
+LsaIGetSerialNumberPolicy2(
+ IN LSAPR_HANDLE PolicyHandle,
+ OUT PLARGE_INTEGER ModifiedCount,
+ OUT PLARGE_INTEGER ModifiedCountAtLastPromotion,
+ OUT PLARGE_INTEGER CreationTime
+ )
+
+/*++
+
+Routine Description:
+
+ This service retrieves the creation time and the current count of
+ modifications to the LSA Database. This information is used as
+ a serial number for the LSA Database.
+
+Arguments:
+
+ PolicyHandle - Trusted handle to Policy object obtained from
+ LsaIOpenPolicyTrusted().
+
+ ModifiedCount - Receives the current count of modifications to the
+ LSA's database.
+
+ ModifiedCountAtLastPromotion - Receives the modified count the last
+ time this machine was promoted to primary domain controller.
+
+ CreationTime - Receives the date/time at which the LSA database
+ was created.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ Same as LsarQueryInformationPolicy.
+--*/
+
+{
+ NTSTATUS Status;
+ PPOLICY_MODIFICATION_INFO PolicyModificationInfo = NULL;
+
+
+ //
+ // Query the Policy Modification and internal Information.
+ // Note that only a handle marked as Trusted will be accepted.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ PolicyHandle,
+ PolicyModificationInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyModificationInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto GetSerialNumberPolicyError;
+ }
+
+
+GetSerialNumberPolicyFinish:
+
+
+ if (PolicyModificationInfo != NULL) {
+
+ *ModifiedCount = PolicyModificationInfo->ModifiedId;
+ *CreationTime = PolicyModificationInfo->DatabaseCreationTime;
+ MIDL_user_free( PolicyModificationInfo );
+ }
+
+ *ModifiedCountAtLastPromotion = LsapDbState.ModifiedIdAtLastPromotion;
+
+ return (Status);
+
+GetSerialNumberPolicyError:
+
+ goto GetSerialNumberPolicyFinish;
+}
+
+
+NTSTATUS
+LsaISetSerialNumberPolicy(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLARGE_INTEGER ModifiedCount,
+ IN PLARGE_INTEGER CreationTime,
+ IN BOOLEAN StartOfFullSync
+ )
+
+/*++
+
+Routine Description:
+
+ Thin wrapper around LsaISetSerialNumberPolicy2().
+ See that function for descriptions.
+
+--*/
+
+{
+
+ return( LsaISetSerialNumberPolicy2( PolicyHandle,
+ ModifiedCount,
+ NULL,
+ CreationTime,
+ StartOfFullSync
+ ) );
+}
+
+
+NTSTATUS
+LsaISetSerialNumberPolicy2(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLARGE_INTEGER ModifiedCount,
+ IN PLARGE_INTEGER ModifiedCountAtLastPromotion OPTIONAL,
+ IN PLARGE_INTEGER CreationTime,
+ IN BOOLEAN StartOfFullSync
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ PolicyHandle - Trusted handle to Policy object obtained from
+ LsaIOpenPolicyTrusted().
+
+ ModifiedCount - Provides the current count of modifications to the
+ LSA's database.
+
+ ModifiedCountAtLastPromotion - If present, provides a new
+ ModifiedIdAtLastPromotion value for the LSA database.
+
+ CreationTime - Provides the date/time at which the LSA database
+ was created.
+
+ StartOfFullSync - This boolean indicates whether a full sync is
+ being initiated. If TRUE is specified, then a full sync is to
+ follow and all existing LSA database information will be discarded.
+ If FALSE is specified, then only specific LSA Database information
+ is to follow and all changes must comply with standard LSA
+ operation behavior.
+
+ NOTE: This parameter is not currently used. It is designed
+ in for possible future use.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LARGE_INTEGER AdjustedModifiedId;
+ LARGE_INTEGER One = {1,0};
+ BOOLEAN ObjectReferenced = FALSE;
+ POLICY_MODIFICATION_INFO OriginalPolicyModificationInfo;
+ LARGE_INTEGER OriginalModifiedIdAtLastPromot;
+
+ OriginalPolicyModificationInfo = LsapDbState.PolicyModificationInfo;
+ OriginalModifiedIdAtLastPromot = LsapDbState.ModifiedIdAtLastPromotion;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the handle is
+ // a valid trusted handle to the Policy Object.
+ // Reference the handle and start an Lsa Database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ (ACCESS_MASK) 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION | LSAP_DB_TRUSTED
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSerialNumberPolicyError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Set the Modified Id to the value specified. We have to do this
+ // in two different ways, depending on whether or not the target
+ // system is configured as a Backup Domain Controller. This is necessary
+ // because for a write transaction in the non-Backup Domain Controller
+ // case, the Modified Id is automatically incremented if the transaction
+ // is successfully committed when the Policy Handle is dereferenced.
+ //
+ // Case - Backup Domain Controller
+ //
+ // We explicitly add a transaction log entry to set the Modified Id to
+ // the desired value.
+ //
+
+ if (LsapDbState.PolicyLsaServerRoleInfo.LsaServerRole ==
+ PolicyServerRoleBackup) {
+
+ //
+ // The target system is configured as a Backup Domain Controller.
+ // This is the common case, where a Replicator is finishing up
+ // an update to the BDC. We explicitly add a transaction log entry
+ // to set the Modified Id to the desired value.
+ //
+
+ LsapDbState.PolicyModificationInfo.ModifiedId = *ModifiedCount;
+ LsapDbState.PolicyModificationInfo.DatabaseCreationTime = *CreationTime;
+
+ Status = LsapDbWriteAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[ PolMod ],
+ (PVOID) &LsapDbState.PolicyModificationInfo,
+ (ULONG) sizeof (POLICY_MODIFICATION_INFO)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto SetSerialNumberPolicyError;
+ }
+
+
+ } else {
+
+ //
+ // The target system is not configured as Backup Domain Controller.
+ // Set the in-memory copy of the Modified Id to the desired value
+ // minus one. The transaction log is empty at this point, but the
+ // routine LsapDbApplyTransaction() automatically increments
+ // the in-memory Modified Id and then adds an entry to the transaction
+ // log to write the Modified Id to the database. The net effect is
+ // therefore to set the Modified Id to the value specified.
+ //
+
+ AdjustedModifiedId.QuadPart = ModifiedCount->QuadPart - One.QuadPart;
+
+ //
+ //
+ // Set the Policy Modification Information local copy. When we
+ // commit the transaction, the database copy will be updated.
+ //
+
+ LsapDbState.PolicyModificationInfo.ModifiedId = AdjustedModifiedId;
+ LsapDbState.PolicyModificationInfo.DatabaseCreationTime = *CreationTime;
+ if (ARGUMENT_PRESENT(ModifiedCountAtLastPromotion)) {
+ LsapDbState.ModifiedIdAtLastPromotion = *ModifiedCountAtLastPromotion;
+ }
+ }
+
+ if (ARGUMENT_PRESENT(ModifiedCountAtLastPromotion)) {
+ LsapDbState.ModifiedIdAtLastPromotion = *ModifiedCountAtLastPromotion;
+ Status = LsapDbWriteAttributeObject(
+ LsapDbHandle,
+ &LsapDbNames[ PolPromot ],
+ (PVOID) &LsapDbState.ModifiedIdAtLastPromotion,
+ (ULONG) sizeof (LARGE_INTEGER)
+ );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ goto SetSerialNumberPolicyError;
+ }
+
+ //
+ // Invalidate the cache for the Policy Modification Information
+ //
+
+ LsapDbMakeInvalidInformationPolicy( PolicyModificationInformation );
+
+SetSerialNumberPolicyFinish:
+
+ //
+ // If necessary, finish any Lsa Database transaction, notify the
+ // LSA Database Replicator of the change, dereference the Policy Object,
+ // release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbChange,
+ Status
+ );
+
+ ObjectReferenced = FALSE;
+
+ }
+
+ return (Status);
+
+SetSerialNumberPolicyError:
+
+ //
+ // Attempt to restore the Serial Number to its original value.
+ // We need only reset the in-memory copy.
+ //
+
+ LsapDbState.PolicyModificationInfo = OriginalPolicyModificationInfo;
+ LsapDbState.ModifiedIdAtLastPromotion = OriginalModifiedIdAtLastPromot;
+
+ goto SetSerialNumberPolicyFinish;
+
+ //
+ // Although the StartOfFullSync parameter is included in the specification
+ // of this API, it has currently been designed out. The original
+ // intent was to disable non-Trusted access to the Policy Database
+ // while a full sync was taking place, but such a sync is currently
+ // a non-atomic operation.
+ //
+
+ UNREFERENCED_PARAMETER( StartOfFullSync );
+}
+
+
+NTSTATUS
+LsapDbNotifyRoleChangePolicy(
+ IN POLICY_LSA_SERVER_ROLE NewRole
+ )
+
+/*++
+
+Routine Description:
+
+ This function notifies an Lsa Database Replicator of a change in the
+ role of the local machine. This change is relevant only when the
+ machine is a Domain Controller.
+
+Arguments:
+
+ NewRole - Specifies the new server role.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ Result codes from I_NetNotifyRole
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // If Replication Notification is enabled, notify the replicator of the
+ // change.
+ //
+
+ Status = I_NetNotifyRole( NewRole );
+
+ //
+ // Suppress the Result Code from I_NetNotifyRole. Currently there is
+ // no meaningful action an LSA client of this routine can take if
+ // an error occurs.
+ //
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+LsapDbBuildPolicyCache(
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs a cache for the Policy object. The cache
+ consists of a suingle structure containing fixed length attributes
+ of the Policy object directly, and pointers or Top level structures
+ for (variable length attributes.
+
+ NOTE: Currently, only the PolicyDefaultQuotaInformation information
+ class has information in the Policy object Cache.
+
+Arguments:
+
+ None
+
+Return Values:
+
+ None
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ POLICY_INFORMATION_CLASS InformationClass;
+
+ //
+ // Do a slow query of each attribute in turn.
+ //
+
+ for ( InformationClass = PolicyAuditLogInformation;
+ InformationClass <= PolicyAuditFullQueryInformation;
+ InformationClass++ ) {
+
+ if (InformationClass == PolicyAuditFullSetInformation) {
+
+ continue;
+ }
+
+ Status = LsapDbSlowQueryInformationPolicy(
+ LsapPolicyHandle,
+ InformationClass,
+ &LsapDbPolicy.Info[InformationClass].Attribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ LsapDbPolicy.Info[InformationClass].AttributeLength = 0;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto BuildPolicyCacheError;
+ }
+
+ //
+ // Store buffer lengths top level nodes of returned information.
+ //
+
+ LsapDbPolicy.Info[PolicyAuditLogInformation].AttributeLength
+ = sizeof(POLICY_AUDIT_LOG_INFO);
+ LsapDbPolicy.Info[PolicyAuditEventsInformation].AttributeLength
+ = sizeof(LSAPR_POLICY_AUDIT_EVENTS_INFO);
+ LsapDbPolicy.Info[PolicyPrimaryDomainInformation].AttributeLength
+ = sizeof(LSAPR_POLICY_PRIMARY_DOM_INFO);
+ LsapDbPolicy.Info[PolicyAccountDomainInformation].AttributeLength
+ = sizeof(LSAPR_POLICY_ACCOUNT_DOM_INFO);
+ LsapDbPolicy.Info[PolicyPdAccountInformation].AttributeLength
+ = sizeof(LSAPR_POLICY_PD_ACCOUNT_INFO);
+ LsapDbPolicy.Info[PolicyLsaServerRoleInformation].AttributeLength
+ = sizeof(POLICY_LSA_SERVER_ROLE_INFO);
+ LsapDbPolicy.Info[PolicyReplicaSourceInformation].AttributeLength
+ = sizeof(LSAPR_POLICY_REPLICA_SRCE_INFO);
+ LsapDbPolicy.Info[PolicyDefaultQuotaInformation].AttributeLength
+ = sizeof(POLICY_DEFAULT_QUOTA_INFO);
+ LsapDbPolicy.Info[PolicyModificationInformation].AttributeLength
+ = sizeof(POLICY_MODIFICATION_INFO);
+ LsapDbPolicy.Info[PolicyAuditFullSetInformation].AttributeLength
+ = sizeof(POLICY_AUDIT_FULL_SET_INFO);
+ LsapDbPolicy.Info[PolicyAuditFullQueryInformation].AttributeLength
+ = sizeof(POLICY_AUDIT_FULL_QUERY_INFO);
+
+BuildPolicyCacheFinish:
+
+ return(Status);
+
+BuildPolicyCacheError:
+
+ goto BuildPolicyCacheFinish;
+}
+
+
+NTSTATUS
+LsapDbUpdateInformationPolicy(
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN OPTIONAL PLSAPR_POLICY_INFORMATION PolicyInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function updates the Policy Object Cache for a particular information
+ class. When a set of the information for a given class occurs, the
+ old information stored in the Policy Object Cache for that class is marked
+ invalid and freed. Next time a query is done for that class, this
+ routine is called to restore the information from backing storage.
+
+Arguments:
+
+ InformationClass - Specifies the type of information being changed.
+ See LsapDbQueryInformationPolicy for details.
+
+ Buffer - Points to a structure containing the new information.
+ If NULL is specified, the information will be updated from backing
+ storage. NOTE: Currently, only NULL may be specified.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG TopNodeLength;
+
+ //
+ // Just query the information back from disk to the cache.
+ //
+
+ Status = LsapDbSlowQueryInformationPolicy(
+ LsapPolicyHandle,
+ InformationClass,
+ &LsapDbPolicy.Info[InformationClass].Attribute
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto UpdateInformationPolicyError;
+ }
+
+ //
+ // Now compute and store the length of the top node.
+ //
+
+ switch (InformationClass) {
+
+ case PolicyAuditLogInformation :
+
+ TopNodeLength = sizeof(POLICY_AUDIT_LOG_INFO);
+ break;
+
+ case PolicyAuditEventsInformation :
+
+ TopNodeLength = sizeof(LSAPR_POLICY_AUDIT_EVENTS_INFO);
+ break;
+
+ case PolicyPrimaryDomainInformation :
+
+ TopNodeLength = sizeof(LSAPR_POLICY_PRIMARY_DOM_INFO);
+ break;
+
+ case PolicyAccountDomainInformation :
+
+ TopNodeLength = sizeof(LSAPR_POLICY_ACCOUNT_DOM_INFO);
+ break;
+
+ case PolicyPdAccountInformation :
+
+ TopNodeLength = sizeof(LSAPR_POLICY_PD_ACCOUNT_INFO);
+ break;
+
+ case PolicyLsaServerRoleInformation :
+
+ TopNodeLength = sizeof(POLICY_LSA_SERVER_ROLE_INFO);
+ break;
+
+ case PolicyReplicaSourceInformation :
+
+ TopNodeLength = sizeof(LSAPR_POLICY_REPLICA_SRCE_INFO);
+ break;
+
+ case PolicyDefaultQuotaInformation :
+
+ TopNodeLength = sizeof(POLICY_DEFAULT_QUOTA_INFO);
+ break;
+
+ case PolicyModificationInformation :
+
+ TopNodeLength = sizeof(POLICY_MODIFICATION_INFO);
+ break;
+
+ case PolicyAuditFullSetInformation :
+
+ TopNodeLength = 0;
+ break;
+
+ case PolicyAuditFullQueryInformation :
+
+ TopNodeLength = sizeof(POLICY_AUDIT_FULL_QUERY_INFO);
+ break;
+ }
+
+ LsapDbPolicy.Info[ InformationClass].AttributeLength = TopNodeLength;
+
+UpdateInformationPolicyFinish:
+
+ return(Status);
+
+UpdateInformationPolicyError:
+
+ goto UpdateInformationPolicyFinish;
+}
+
diff --git a/private/lsa/server/dbpriv.c b/private/lsa/server/dbpriv.c
new file mode 100644
index 000000000..193d3835b
--- /dev/null
+++ b/private/lsa/server/dbpriv.c
@@ -0,0 +1,2572 @@
+
+/*++
+
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbpriv.c
+
+Abstract:
+
+ LSA - Database - Privilege Object Private API Workers
+
+ NOTE: This module should remain as portable code that is independent
+ of the implementation of the LSA Database. As such, it is
+ permitted to use only the exported LSA Database interfaces
+ contained in db.h and NOT the private implementation
+ dependent functions in dbp.h.
+
+Author:
+
+ Jim Kelly (JimK) March 24, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+#include "adtp.h"
+#include <windef.h>
+#include <winnls.h>
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Module-wide data types //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+typedef struct _LSAP_DLL_DESCRIPTOR {
+ WORD Count;
+ WORD Language;
+ PVOID DllHandle;
+} LSAP_DLL_DESCRIPTOR, *PLSAP_DLL_DESCRIPTOR;
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Module-wide variables //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Neutral English language value
+//
+
+WORD LsapNeutralEnglish;
+
+//
+// Until we actually have a privilege object, keep well known privilege
+// information as global data. The information for each privilege is
+// kept in a an array POLICY_PRIVILEGE_DEFINITION structures.
+//
+
+ULONG LsapWellKnownPrivilegeCount;
+POLICY_PRIVILEGE_DEFINITION LsapKnownPrivilege[SE_MAX_WELL_KNOWN_PRIVILEGE];
+
+
+//
+// Array of handles to DLLs containing privilege definitions
+//
+
+ULONG LsapPrivilegeDllCount;
+PLSAP_DLL_DESCRIPTOR LsapPrivilegeDlls; //Array
+
+
+
+//
+// TEMPORARY: Name of Microsoft's standard privilege names DLL
+//
+
+WCHAR MsDllNameString[] = L"msprivs";
+UNICODE_STRING MsDllName;
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Module Wide Macros //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+//
+//NTSTATUS
+//LsapFreePrivilegeDllNames(
+// IN PUNICODE_STRING DllNames
+// )
+//
+
+#define LsapFreePrivilegeDllNames( D ) (STATUS_SUCCESS)
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Internal routine templates //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapLookupKnownPrivilegeName(
+ PLUID Value,
+ PUNICODE_STRING *Name
+ );
+
+NTSTATUS
+LsapLookupKnownPrivilegeValue(
+ PUNICODE_STRING Name,
+ PLUID Value
+ );
+
+NTSTATUS
+LsapLookupPrivilegeDisplayName(
+ IN PUNICODE_STRING ProgrammaticName,
+ IN WORD ClientLanguage,
+ IN WORD ClientSystemDefaultLanguage,
+ OUT PUNICODE_STRING *DisplayName,
+ OUT PWORD LanguageReturned
+ );
+
+
+NTSTATUS
+LsapGetPrivilegeDisplayName(
+ IN ULONG DllIndex,
+ IN ULONG PrivilegeIndex,
+ IN WORD ClientLanguage,
+ IN WORD ClientSystemDefaultLanguage,
+ OUT PUNICODE_STRING *DisplayName,
+ OUT PWORD LanguageReturned
+ );
+
+
+NTSTATUS
+LsapGetPrivilegeIndex(
+ IN PUNICODE_STRING Name,
+ IN ULONG DllIndex,
+ OUT PULONG PrivilegeIndex
+ );
+
+
+VOID
+LsapGetDisplayTable(
+ IN ULONG DllIndex,
+ IN WORD ClientLanguage,
+ IN WORD ClientSystemDefaultLanguage,
+ OUT PWORD LanguageReturned,
+ OUT PWORD *DisplayTable
+ );
+
+
+NTSTATUS
+LsapCopyDisplayPrivilegeText(
+ IN PWORD DisplayTable,
+ IN ULONG PrivilegeIndex,
+ OUT PUNICODE_STRING *DisplayName
+ );
+
+
+NTSTATUS
+LsapOpenPrivilegeDlls( VOID );
+
+
+NTSTATUS
+LsapGetPrivilegeDllNames(
+ OUT PUNICODE_STRING *DllNames,
+ OUT PULONG DllCount
+ );
+
+
+NTSTATUS
+LsapValidatePrivilegeDlls( VOID );
+
+
+NTSTATUS
+LsapValidateProgrammaticNames(
+ ULONG DllIndex
+ );
+
+NTSTATUS
+LsapDbInitWellKnownPrivilegeName(
+ IN ULONG Index,
+ IN PUNICODE_STRING Name
+ );
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// RPC stub-called routines //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsarEnumeratePrivileges(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function returnes information about privileges known on this
+ system. This call requires POLICY_VIEW_LOCAL_INFORMATION access
+ to the Policy Object. Since there may be more information than
+ can be returned in a single call of the routine, multiple calls
+ can be made to get all of the information. To support this feature,
+ the caller is provided with a handle that can be used across calls to
+ the API. On the initial call, EnumerationContext should point to a
+ variable that has been initialized to 0.
+
+ WARNING! CURRENTLY, THIS FUNCTION ONLY RETURNS INFORMATION ABOUT
+ WELL-KNOWN PRIVILEGES. LATER, IT WILL RETURN INFORMATION
+ ABOUT LOADED PRIVILEGES.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsarOpenPolicy() call.
+
+ EnumerationContext - API specific handle to allow multiple calls
+ (see Routine Description).
+
+ EnumerationBuffer - Pointer to structure that will be initialized to
+ contain a count of the privileges returned and a pointer to an
+ array of structures of type LSAPR_POLICY_PRIVILEGE_DEF describing
+ the privileges.
+
+ PreferedMaximumLength - Prefered maximim length of returned data
+ (in 8-bit bytes). This is not a hard upper limit, but serves as
+ a guide. Due to data conversion between systems with different
+ natural data sizes, the actual amount of data returned may be
+ greater than this value.
+
+ CountReturned - Number of entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
+ a Policy object.
+
+ STATUS_ACCESS_DENIED - The caller does not have the necessary
+ access to perform the operation.
+
+ STATUS_MORE_ENTRIES - There are more entries, so call again. This
+ is an informational status only.
+
+ STATUS_NO_MORE_ENTRIES - No entries were returned because there
+ are no more.
+--*/
+
+{
+ NTSTATUS Status, PreliminaryStatus;
+ BOOLEAN ObjectReferenced = FALSE;
+
+ //
+ // Acquire the Lsa Database lock. Verify that PolicyHandle is a valid
+ // hadnle to a Policy Object and is trusted or has the necessary accesses.
+ // Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto EnumeratePrivilegesError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Call Privilege Enumeration Routine.
+ //
+
+ Status = LsapDbEnumeratePrivileges(
+ EnumerationContext,
+ EnumerationBuffer,
+ PreferedMaximumLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto EnumeratePrivilegesError;
+ }
+
+EnumeratePrivilegesFinish:
+
+ //
+ // If necessary, dereference the Policy Object, release the LSA Database
+ // lock and return. Preserve current Status where appropriate.
+ //
+
+ if (ObjectReferenced) {
+
+ PreliminaryStatus = Status;
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ PreliminaryStatus
+ );
+
+ ObjectReferenced = FALSE;
+
+ if ((!NT_SUCCESS(Status)) && NT_SUCCESS(PreliminaryStatus)) {
+
+ goto EnumeratePrivilegesError;
+ }
+ }
+
+ return(Status);
+
+EnumeratePrivilegesError:
+
+ goto EnumeratePrivilegesFinish;
+}
+
+
+NTSTATUS
+LsapDbEnumeratePrivileges(
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
+ IN ULONG PreferedMaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function returnes information about the Privileges that exist
+ in the system.access to the Policy Object. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information.
+ To support this feature, the caller is provided with a handle that can
+ be used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0.
+
+ WARNING! CURRENTLY, THIS FUNCTION ONLY RETURNS INFORMATION ABOUT
+ WELL-KNOWN PRIVILEGES. LATER, IT WILL RETURN INFORMATION
+ ABOUT LOADED PRIVILEGES.
+
+Arguments:
+
+ EnumerationContext - API specific handle to allow multiple calls
+ (see Routine Description).
+
+ EnumerationBuffer - Pointer to structure that will be initialized to
+ contain a count of the privileges returned and a pointer to an
+ array of structures of type LSAPR_POLICY_PRIVILEGE_DEF describing
+ the privileges.
+
+ PreferedMaximumLength - Prefered maximim length of returned data
+ (in 8-bit bytes). This is not a hard upper limit, but serves as
+ a guide. Due to data conversion between systems with different
+ natural data sizes, the actual amount of data returned may be
+ greater than this value.
+
+ CountReturned - Number of entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
+ a Policy object.
+
+ STATUS_ACCESS_DENIED - The caller does not have the necessary
+ access to perform the operation.
+
+ STATUS_MORE_ENTRIES - There are more entries, so call again. This
+ is an informational status only.
+
+ STATUS_NO_MORE_ENTRIES - No entries were returned because there
+ are no more.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG WellKnownPrivilegeCount = (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE + 1);
+ ULONG Index;
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ //
+ // If the Enumeration Context Value given exceeds the total count of
+ // Privileges, return an error.
+ //
+
+ Status = STATUS_NO_MORE_ENTRIES;
+
+ if (*EnumerationContext >= WellKnownPrivilegeCount) {
+
+ goto EnumeratePrivilegesError;
+ }
+
+ //
+ // Since there are only a small number of privileges, we will
+ // return all of the information in one go, so allocate memory
+ // for output array of Privilege Definition structures.
+ //
+
+ EnumerationBuffer->Entries = WellKnownPrivilegeCount;
+ EnumerationBuffer->Privileges =
+ MIDL_user_allocate(
+ WellKnownPrivilegeCount * sizeof (POLICY_PRIVILEGE_DEFINITION)
+ );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (EnumerationBuffer->Privileges == NULL) {
+
+ goto EnumeratePrivilegesError;
+ }
+
+ RtlZeroMemory(
+ EnumerationBuffer->Privileges,
+ WellKnownPrivilegeCount * sizeof (POLICY_PRIVILEGE_DEFINITION)
+ );
+
+ //
+ // Now lookup each of the Well Known Privileges.
+ //
+
+ for( Index = *EnumerationContext;
+ Index < (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE + 1);
+ Index++) {
+
+ EnumerationBuffer->Privileges[ Index ].LocalValue
+ = LsapKnownPrivilege[ Index ].LocalValue;
+
+ Status = LsapRpcCopyUnicodeString(
+ NULL,
+ (PUNICODE_STRING) &EnumerationBuffer->Privileges[ Index].Name,
+ &LsapKnownPrivilege[ Index ].Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto EnumeratePrivilegesError;
+ }
+
+ *EnumerationContext = Index;
+
+EnumeratePrivilegesFinish:
+
+ return(Status);
+
+EnumeratePrivilegesError:
+
+ //
+ // If necessary, free any memory buffers allocated for Well Known Privilege
+ // Programmatic Names.
+ //
+
+ if (EnumerationBuffer->Privileges != NULL) {
+
+ for( Index = 0;
+ Index < SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE;
+ Index++) {
+
+ if ( EnumerationBuffer->Privileges[ Index].Name.Buffer != NULL) {
+
+ MIDL_user_free( EnumerationBuffer->Privileges[ Index ].Name.Buffer );
+ }
+ }
+
+ MIDL_user_free( EnumerationBuffer->Privileges );
+ EnumerationBuffer->Privileges = NULL;
+ }
+
+ EnumerationBuffer->Entries = 0;
+ *EnumerationContext = 0;
+ goto EnumeratePrivilegesFinish;
+
+ UNREFERENCED_PARAMETER( PreferedMaximumLength );
+}
+
+
+NTSTATUS
+LsarLookupPrivilegeValue(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_UNICODE_STRING Name,
+ OUT PLUID Value
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaLookupPrivilegeValue() API.
+
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
+ must be open for POLICY_LOOKUP_NAMES access.
+
+ Name - Is the privilege's programmatic name.
+
+ Value - Receives the locally unique ID the privilege is known by on the
+ target machine.
+
+Return Value:
+
+ NTSTATUS - The privilege was found and returned.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
+
+ //
+ // Make sure we know what RPC is doing to/for us
+ //
+
+ ASSERT( Name != NULL );
+
+ //
+ // make sure the caller has the appropriate access
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_LOOKUP_NAMES,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ //
+ // No need to hold onto the Policy object after this..
+ // We just needed it for access validation purposes.
+ //
+
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+
+ if (NT_SUCCESS(Status)) {
+
+ if (Name->Buffer == 0 || Name->Length == 0) {
+ return(STATUS_NO_SUCH_PRIVILEGE);
+ }
+
+ Status = LsapLookupKnownPrivilegeValue( (PUNICODE_STRING) Name, Value );
+ }
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+LsarLookupPrivilegeName(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLUID Value,
+ OUT PLSAPR_UNICODE_STRING *Name
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaLookupPrivilegeName() API.
+
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
+ must be open for POLICY_LOOKUP_NAMES access.
+
+ Value - is the locally unique ID the privilege is known by on the
+ target machine.
+
+ Name - Receives the privilege's programmatic name.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The privilege was found and returned.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
+
+ //
+ // make sure we know what RPC is doing to/for us
+ //
+
+ ASSERT( Name != NULL );
+ ASSERT( (*Name) == NULL );
+
+
+ //
+ // make sure the caller has the appropriate access
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_LOOKUP_NAMES,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ //
+ // No need to hold onto the Policy object after this..
+ // We just needed it for access validation purposes.
+ //
+
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsapLookupKnownPrivilegeName( Value,(PUNICODE_STRING *) Name );
+ }
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+LsarLookupPrivilegeDisplayName(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_UNICODE_STRING Name,
+ IN SHORT ClientLanguage,
+ IN SHORT ClientSystemDefaultLanguage,
+ OUT PLSAPR_UNICODE_STRING *DisplayName,
+ OUT PWORD LanguageReturned
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaLookupPrivilegeDisplayName() API.
+
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
+ must be open for POLICY_LOOKUP_NAMES access.
+
+ Name - The programmatic privilege name to look up.
+
+ ClientLanguage - The prefered language to be returned.
+
+ ClientSystemDefaultLanguage - The alternate prefered language
+ to be returned.
+
+ DisplayName - Receives the privilege's displayable name.
+
+ LanguageReturned - The language actually returned.
+
+
+Return Value:
+
+ NTSTATUS - The privilege text was found and returned.
+
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
+
+ //
+ // make sure we know what RPC is doing to/for us
+ //
+
+ ASSERT( DisplayName != NULL );
+ ASSERT( (*DisplayName) == NULL );
+ ASSERT( LanguageReturned != NULL );
+
+
+ //
+ // make sure the caller has the appropriate access
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_LOOKUP_NAMES,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ //
+ // No need to hold onto the Policy object after this..
+ // We just needed it for access validation purposes.
+ //
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ if (Name->Buffer == 0 || Name->Length == 0) {
+ return(STATUS_NO_SUCH_PRIVILEGE);
+ }
+ Status = LsapLookupPrivilegeDisplayName(
+ (PUNICODE_STRING)Name,
+ (WORD)ClientLanguage,
+ (WORD)ClientSystemDefaultLanguage,
+ (PUNICODE_STRING *)DisplayName,
+ LanguageReturned
+ );
+ }
+
+ return(Status);
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// //
+// Internal Routines //
+// //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapLookupKnownPrivilegeName(
+ IN PLUID Value,
+ OUT PUNICODE_STRING *Name
+ )
+
+/*++
+
+Routine Description:
+
+ Look up the specified LUID and return the corresponding
+ privilege's programmatic name (if found).
+
+ FOR NOW, WE ONLY SUPPORT WELL-KNOWN MICROSOFT PRIVILEGES.
+ THESE ARE HARD-CODED HERE. IN THE FUTURE, WE MUST ALSO
+ SEARCH A LIST OF LOADED PRIVILEGES.
+
+Arguments:
+
+ Value - Value to look up.
+
+ Name - Receives the corresponding name - allocated with
+ MIDL_user_allocate() and ready to return via an RPC stub.
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_NO_MEMORY - Indicates there was not enough heap memory available
+ to produce the final TokenInformation structure.
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+
+{
+ ULONG i;
+ PUNICODE_STRING ReturnName;
+
+ for ( i=0; i<LsapWellKnownPrivilegeCount; i++) {
+
+ if (RtlEqualLuid(Value, &LsapKnownPrivilege[i].LocalValue)) {
+
+ ReturnName = MIDL_user_allocate( sizeof(UNICODE_STRING) );
+ if (ReturnName == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ (*ReturnName) = LsapKnownPrivilege[i].Name;
+
+ ReturnName->Buffer = MIDL_user_allocate( ReturnName->MaximumLength );
+ if (ReturnName->Buffer == NULL) {
+ MIDL_user_free( ReturnName );
+ return(STATUS_NO_MEMORY);
+ }
+
+ RtlCopyUnicodeString( ReturnName,
+ &LsapKnownPrivilege[i].Name
+ );
+
+ (*Name) = ReturnName;
+
+ return(STATUS_SUCCESS);
+ }
+ }
+
+ return(STATUS_NO_SUCH_PRIVILEGE);
+}
+
+
+
+NTSTATUS
+LsapLookupKnownPrivilegeValue(
+ PUNICODE_STRING Name,
+ PLUID Value
+ )
+
+/*++
+
+Routine Description:
+
+ Look up the specified name and return the corresponding
+ privilege's locally assigned value (if found).
+
+
+ FOR NOW, WE ONLY SUPPORT WELL-KNOWN MICROSOFT PRIVILEGES.
+ THESE ARE HARD-CODED HERE. IN THE FUTURE, WE MUST ALSO
+ SEARCH A LIST OF LOADED PRIVILEGES.
+
+
+
+Arguments:
+
+
+
+ Name - The name to look up.
+
+ Value - Receives the corresponding locally assigned value.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+
+{
+ ULONG i;
+ BOOLEAN Found;
+
+ for ( i=0; i<LsapWellKnownPrivilegeCount; i++) {
+
+ Found = RtlEqualUnicodeString( Name, &LsapKnownPrivilege[i].Name, TRUE );
+
+ if (Found == TRUE) {
+
+ (*Value) = LsapKnownPrivilege[i].LocalValue;
+ return(STATUS_SUCCESS);
+ }
+ }
+
+ return(STATUS_NO_SUCH_PRIVILEGE);
+}
+
+
+NTSTATUS
+LsapLookupPrivilegeDisplayName(
+ IN PUNICODE_STRING ProgrammaticName,
+ IN WORD ClientLanguage,
+ IN WORD ClientSystemDefaultLanguage,
+ OUT PUNICODE_STRING *DisplayName,
+ OUT PWORD LanguageReturned
+ )
+
+/*++
+
+Routine Description:
+
+ This routine looks through each of the privilege DLLs for the
+ specified privilege. If found, its displayable name is returned.
+
+
+Arguments:
+
+ ProgrammaticName - The programmatic name of the privilege to look up.
+ E.g., "SeTakeOwnershipPrivilege".
+
+ ClientLanguage - The prefered language to be returned.
+
+ ClientSystemDefaultLanguage - The alternate prefered language
+ to be returned.
+
+ DisplayName - receives a pointer to a buffer containing the displayable
+ name associated with the privilege.
+ E.g., "Take ownership of files or other objects".
+
+ The UNICODE_STRING and the buffer pointed to by that structure
+ are individually allocated using MIDL_user_allocate() and must
+ be freed using MIDL_user_free().
+
+ LanguageReturned - The language actually returned.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The name have been successfully retrieved.
+
+ STATUS_NO_MEMORY - Not enough heap was available to return the
+ information.
+
+ STATUS_NO_SUCH_PRIVILEGE - The privilege could not be located
+ in any of the privilege DLLs.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ ULONG DllIndex, PrivilegeIndex;
+
+ for ( DllIndex=0; DllIndex<LsapPrivilegeDllCount; DllIndex++) {
+
+ Status = LsapGetPrivilegeIndex( (PUNICODE_STRING)ProgrammaticName,
+ DllIndex,
+ &PrivilegeIndex
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsapGetPrivilegeDisplayName( DllIndex,
+ PrivilegeIndex,
+ ClientLanguage,
+ ClientSystemDefaultLanguage,
+ DisplayName,
+ LanguageReturned
+ );
+ return(Status);
+ }
+ }
+
+ return(Status);
+
+}
+
+
+
+NTSTATUS
+LsapGetPrivilegeDisplayName(
+ IN ULONG DllIndex,
+ IN ULONG PrivilegeIndex,
+ IN WORD ClientLanguage,
+ IN WORD ClientSystemDefaultLanguage,
+ OUT PUNICODE_STRING *DisplayName,
+ OUT PWORD LanguageReturned
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns a copy of the specified privilege's display name.
+
+ The copy of the name is returned in two buffers allocated using
+ MIDL_user_allocate(). This allows the information to be returned
+ via an RPC service so that RPC generated stubs will correctly free
+ the buffers.
+
+ Every attempt is made to retrieve a language that the client prefers
+ (first the client, then the client's system). Failing this, this
+ routine may return another language (such as the server's default
+ language).
+
+
+Arguments:
+
+ DllIndex - The index of the privilege DLL to use.
+
+ PrivilegeIndex - The index of the privilege entry in the DLL whose
+ display name is to be returned.
+
+ ClientLanguage - The language to return if possible.
+
+ ClientSystemDefaultLanguage - If ClientLanguage couldn't be found, then
+ return this language if possible.
+
+ DisplayName - receives a pointer to a buffer containing the displayable
+ name associated with the privilege.
+
+ The UNICODE_STRING and the buffer pointed to by that structure
+ are individually allocated using MIDL_user_allocate() and must
+ be freed using MIDL_user_free().
+
+ LanguageReturned - Receives the language actually retrieved.
+ If neither ClientLanguage nor ClientSystemDefaultLanguage could be
+ found, then this value may contain yet another value.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The display name has been successfully returned.
+
+ STATUS_NO_MEMORY - Not enough heap was available to return the
+ information.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PWORD DisplayTable;
+
+
+ //
+ // get a pointer to the DISPLAYABLE_PRIVILEGE_TEXT table for
+ // the appropriate language.
+ //
+
+ LsapGetDisplayTable( DllIndex,
+ ClientLanguage,
+ ClientSystemDefaultLanguage,
+ LanguageReturned,
+ &DisplayTable
+ );
+
+ Status = LsapCopyDisplayPrivilegeText( DisplayTable,
+ PrivilegeIndex,
+ DisplayName
+ );
+
+ return(Status);
+
+
+}
+
+
+NTSTATUS
+LsapGetPrivilegeIndex(
+ IN PUNICODE_STRING Name,
+ IN ULONG DllIndex,
+ OUT PULONG PrivilegeIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine looks through a single privilege DLL for the privilege
+ with the name matching that specified by the Name parameter.
+ If found, its index in this DLL is returned.
+
+
+Arguments:
+
+ Name - The programmatic name of the privilege to look up.
+ E.g., "SeTakeOwnershipPrivilege".
+
+ DllIndex - The index of the privilege DLL to look in.
+
+ PrivilegeIndex - Receives the index of the privilege entry in this
+ resource file.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The pivilege has been successfully located.
+
+ STATUS_NO_SUCH_PRIVILEGE - The privilege could not be located.
+
+--*/
+
+
+
+{
+ WORD i;
+ HANDLE ProgrammaticResource, ProgrammaticLoad, ProgrammaticLock;
+ PWORD NextWord;
+ WORD OffsetToNextEntry;
+ UNICODE_STRING TmpName;
+ BOOLEAN NameFound;
+
+
+//DbgPrint("Searching DLL[%ld] for *%Z*...\n", DllIndex, Name);
+
+
+
+ ProgrammaticResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_PROGRAM_NAMES),
+ (WORD)LsapNeutralEnglish
+ );
+ if (ProgrammaticResource == NULL) {
+ ASSERT( NT_SUCCESS(STATUS_INTERNAL_DB_CORRUPTION) );
+ return(STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+
+ ProgrammaticLoad = LoadResource(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ ProgrammaticResource
+ );
+ if (ProgrammaticLoad == NULL) {
+ ASSERT( NT_SUCCESS(STATUS_INTERNAL_DB_CORRUPTION) );
+ return(STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+ ProgrammaticLock = LockResource(ProgrammaticLoad);
+
+ if (ProgrammaticLock == NULL) {
+ ASSERT( NT_SUCCESS(STATUS_INTERNAL_DB_CORRUPTION) );
+ return(STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+ NextWord = (PWORD)ProgrammaticLock;
+
+
+
+
+ //
+ // Walk the list of defined privileges in this DLL looking for
+ // a match.
+ //
+
+ for ( i=0; i<LsapPrivilegeDlls[ DllIndex ].Count; i++) {
+
+ //
+ // Skip index
+ //
+
+ ASSERT( i == (*NextWord) ); // Expect this to be the index;
+ NextWord++;
+ NextWord++;
+
+ //
+ // Save offset to next entry and then
+ // set up a unicode string representing the privilege
+ // name. Make sure NextWord is left pointing at the
+ // beginning of the name buffer.
+ //
+
+ OffsetToNextEntry = (*NextWord);
+
+ TmpName.MaximumLength = (*NextWord++); // Skip the NextOffset field
+ TmpName.Length = (*NextWord++); // Skip the Length field
+ TmpName.Buffer = (PVOID)NextWord;
+
+//DbgPrint(" Comparing to *%Z*\n", &TmpName);
+
+ NameFound = RtlEqualUnicodeString( Name, &TmpName, TRUE );
+
+ if (NameFound == TRUE) {
+ (*PrivilegeIndex) = i;
+ return(STATUS_SUCCESS);
+ }
+
+
+ NextWord = (PWORD)( (PUCHAR)NextWord + OffsetToNextEntry );
+
+ }
+
+ return(STATUS_NO_SUCH_PRIVILEGE);
+
+}
+
+
+
+VOID
+LsapGetDisplayTable(
+ IN ULONG DllIndex,
+ IN WORD ClientLanguage,
+ IN WORD ClientSystemDefaultLanguage,
+ OUT PWORD LanguageReturned,
+ OUT PWORD *DisplayTable
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets a pointer to a display table of the specified
+ privilege DLL. In selecting a language to use, the following
+ preference is given:
+
+ ClientLanguage
+ ClientSystemDefaultLanguage
+ The default language of the privilege DLL
+
+ The last one of these MUST be present in the DLL and so failure
+ is not possible.
+
+
+Arguments:
+
+ DllIndex - The index of the privilege DLL to use.
+
+ ClientLanguage - The first choice language to locate. If the
+ exact language can't be found, then a neutral form of the
+ language is looked for.
+
+ ClientSystemDefaultLanguage - The second choice language to locate.
+ If the exact language can't be found, then a neutral form of
+ the language is looked for.
+
+ LanguageReturned - Receives the language actually located.
+
+ DisplayTable - Receives a pointer to the display table.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ HANDLE DisplayResource, DisplayLoad, DisplayLock;
+ WORD NeutralLanguage;
+
+
+//DbgPrint("Searching DLL[%ld] for display table for language %ld...\n", (ULONG)ClientLanguage);
+
+
+
+ DisplayResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_DISPLAY_NAMES),
+ ClientLanguage
+ );
+ (*LanguageReturned) = ClientLanguage;
+
+
+ if (DisplayResource == NULL) {
+
+ //
+ // How about a neutral form of the language?
+ //
+
+ NeutralLanguage = MAKELANGID( PRIMARYLANGID(ClientLanguage),
+ SUBLANG_NEUTRAL);
+
+ if (NeutralLanguage != ClientLanguage) {
+
+ DisplayResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_DISPLAY_NAMES),
+ NeutralLanguage
+ );
+ (*LanguageReturned) = NeutralLanguage;
+ }
+ }
+
+
+ if (DisplayResource == NULL) {
+
+ //
+ // Well, ok. How about the client's system's default lang?
+ //
+
+ if (ClientLanguage != ClientSystemDefaultLanguage) {
+
+ DisplayResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_DISPLAY_NAMES),
+ ClientSystemDefaultLanguage
+ );
+ (*LanguageReturned) = ClientSystemDefaultLanguage;
+ }
+ }
+
+
+ if (DisplayResource == NULL) {
+
+ //
+ // Not very lucky so far. How about a neutral form
+ // of the system's default language?
+ //
+
+ NeutralLanguage = MAKELANGID( PRIMARYLANGID(ClientSystemDefaultLanguage),
+ SUBLANG_NEUTRAL);
+
+ if (NeutralLanguage != ClientLanguage) {
+
+ DisplayResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_DISPLAY_NAMES),
+ NeutralLanguage
+ );
+ (*LanguageReturned) = NeutralLanguage;
+ }
+ }
+
+
+ if (DisplayResource == NULL) {
+
+ //
+ // Hmmm - ok, go with the DLL default language (must exist)
+ //
+
+
+ DisplayResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_DISPLAY_NAMES),
+ LsapPrivilegeDlls[ DllIndex ].Language
+ );
+ (*LanguageReturned) = LsapPrivilegeDlls[ DllIndex ].Language;
+
+ }
+
+ ASSERT(DisplayResource != NULL);
+
+ DisplayLoad = LoadResource(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ DisplayResource
+ );
+ ASSERT (DisplayLoad != NULL);
+
+ DisplayLock = LockResource(DisplayLoad);
+
+ ASSERT (DisplayLock != NULL);
+
+ (*DisplayTable) = (PWORD)DisplayLock;
+
+ return;
+}
+
+
+
+NTSTATUS
+LsapCopyDisplayPrivilegeText(
+ IN PWORD DisplayTable,
+ IN ULONG PrivilegeIndex,
+ OUT PUNICODE_STRING *DisplayName
+ )
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+ DisplayTable - A pointer to the display table to reference.
+
+ PrivilegeIndex - The index of the privilege entry in the DLL whose
+ display name is to be returned.
+
+ DisplayName - receives a pointer to a buffer containing the displayable
+ name associated with the privilege.
+
+ The UNICODE_STRING and the buffer pointed to by that structure
+ are individually allocated using MIDL_user_allocate() and must
+ be freed using MIDL_user_free().
+
+
+Return Value:
+
+ STATUS_SUCCESS - The pivilege has been successfully located.
+
+
+--*/
+
+{
+
+ ULONG i;
+ PWORD NextWord;
+ UNICODE_STRING NameInTable;
+ WORD OffsetToNextEntry;
+ PUNICODE_STRING ReturnName;
+
+ NextWord = DisplayTable;
+
+ //
+ // Walk through the display table until we get the right
+ // privilege entry.
+ //
+
+ for ( i=0; i<PrivilegeIndex; i++) {
+
+ //
+ // Skip index
+ //
+
+ ASSERT( i == (ULONG)(*NextWord) ); // Expect this to be the index;
+ NextWord++; // Skip index
+ NextWord++; // both words of it.
+
+ //
+ // Add offset to next element
+ //
+
+ OffsetToNextEntry = (*NextWord);
+ NextWord++; // Skip the NextOffset field
+ NextWord++; // Skip the Length field
+
+ NextWord = (PWORD)( (PUCHAR)NextWord + OffsetToNextEntry );
+
+
+ }
+
+
+ //
+ // OK, now we are at the right element
+ //
+
+ ASSERT( PrivilegeIndex == (ULONG)(*NextWord) );
+ NextWord++; // Skip index
+ NextWord++; // both words of it.
+
+ NameInTable.MaximumLength = (*NextWord++);
+ NameInTable.Length = (*NextWord++);
+ NameInTable.Buffer = NextWord;
+
+ ReturnName = MIDL_user_allocate( sizeof(UNICODE_STRING) );
+ if (ReturnName == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ ReturnName->Buffer = MIDL_user_allocate( NameInTable.MaximumLength );
+ if (ReturnName->Buffer == NULL) {
+ MIDL_user_free( ReturnName );
+ return(STATUS_NO_MEMORY);
+ }
+
+ ReturnName->MaximumLength = NameInTable.Length;
+ RtlCopyUnicodeString( ReturnName, &NameInTable );
+
+ (*DisplayName) = ReturnName;
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+NTSTATUS
+LsapDbInitializePrivilegeObject( VOID )
+
+/*++
+
+Routine Description:
+
+ This function performs initialization functions related to
+ the LSA privilege object.
+
+ This includes:
+
+ Initializing some variables.
+
+ Loading DLLs containing displayable privilege names.
+
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - The names have been successfully retrieved.
+
+ STATUS_NO_MEMORY - Not enough memory was available to initialize.
+
+--*/
+
+{
+ NTSTATUS
+ Status,
+ NtStatus;
+
+ ULONG
+ i;
+
+ LsapNeutralEnglish = MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL);
+
+
+ Status = LsapOpenPrivilegeDlls( );
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ DbgPrint("\n");
+ DbgPrint(" LSASS: Failed loading privilege display name DLLs.\n");
+ DbgPrint(" This is not fatal, but may cause some peculiarities\n");
+ DbgPrint(" in User Interfaces that display privileges.\n\n");
+#endif //DBG
+ return(Status);
+ }
+
+
+ //
+ // Now set up our internal well-known privilege LUID to programmatic name
+ // mapping.
+ //
+
+ i=0;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_CREATE_TOKEN_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_CREATE_TOKEN_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name);
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_LOCK_MEMORY_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_LOCK_MEMORY_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_INCREASE_QUOTA_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_INCREASE_QUOTA_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_MACHINE_ACCOUNT_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_MACHINE_ACCOUNT_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_TCB_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_TCB_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_SECURITY_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_TAKE_OWNERSHIP_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_TAKE_OWNERSHIP_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_LOAD_DRIVER_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_LOAD_DRIVER_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_SYSTEM_PROFILE_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_SYSTEM_PROFILE_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_SYSTEMTIME_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_SYSTEMTIME_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_PROF_SINGLE_PROCESS_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_PROF_SINGLE_PROCESS_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_INC_BASE_PRIORITY_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_INC_BASE_PRIORITY_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_CREATE_PAGEFILE_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_CREATE_PAGEFILE_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_CREATE_PERMANENT_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_CREATE_PERMANENT_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_BACKUP_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_BACKUP_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_RESTORE_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_SHUTDOWN_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_SHUTDOWN_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_DEBUG_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_DEBUG_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_AUDIT_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_AUDIT_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_SYSTEM_ENVIRONMENT_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_SYSTEM_ENVIRONMENT_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_CHANGE_NOTIFY_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_CHANGE_NOTIFY_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ i++;
+ LsapKnownPrivilege[i].LocalValue = RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE);
+ NtStatus = LsapDbInitWellKnownPrivilegeName( SE_REMOTE_SHUTDOWN_PRIVILEGE,
+ &LsapKnownPrivilege[i].Name );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ i++;
+ LsapWellKnownPrivilegeCount = i;
+
+ ASSERT( i == (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE +1));
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapOpenPrivilegeDlls( )
+
+/*++
+
+Routine Description:
+
+ This function opens all the privilege DLLs that it can.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - The names have been successfully retrieved.
+
+ STATUS_NO_MEMORY - Not enough heap was available to return the
+ information.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ ULONG PotentialDlls, FoundDlls, i;
+ PUNICODE_STRING DllNames;
+
+ //
+ // Get the names of the DLLs out of the registry
+ //
+
+ Status = LsapGetPrivilegeDllNames( &DllNames, &PotentialDlls );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // Allocate enough memory to hold handles to all potential DLLs.
+ //
+
+
+ LsapPrivilegeDlls = RtlAllocateHeap(
+ RtlProcessHeap(), 0,
+ PotentialDlls*sizeof(LSAP_DLL_DESCRIPTOR)
+ );
+ if (LsapPrivilegeDlls == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ FoundDlls = 0;
+ for ( i=0; i<PotentialDlls; i++) {
+ Status = LdrLoadDll(
+ NULL,
+ NULL,
+ &DllNames[i],
+ &LsapPrivilegeDlls[FoundDlls].DllHandle
+ );
+
+ if (NT_SUCCESS(Status)) {
+ FoundDlls++;
+ }
+ }
+
+
+ LsapPrivilegeDllCount = FoundDlls;
+
+#if DBG
+ if (FoundDlls == 0) {
+ DbgPrint("\n");
+ DbgPrint("LSASS: Zero privilege DLLs loaded. We expected at\n");
+ DbgPrint(" least msvprivs.dll to be loaded. Privilege\n");
+ DbgPrint(" names will not be displayed at UI properly.\n\n");
+
+ }
+#endif //DBG
+
+
+ //
+ //
+ // !!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!
+ //
+ // Before supporting user loadable privilege DLLs, we must add
+ // code here to validate the structure of the loaded DLL. This
+ // is necessary to prevent an invalid privilege DLL structure
+ // from causing us to crash.
+ //
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //
+
+ //
+ // This routine validates the structure of each loaded DLL.
+ // Any found to be invalid will be logged and discarded.
+ //
+
+ Status = LsapValidatePrivilegeDlls();
+
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapGetPrivilegeDllNames(
+ OUT PUNICODE_STRING *DllNames,
+ OUT PULONG DllCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function obtains the names of DLLs containing privilege
+ definitions from the registry.
+
+
+Arguments:
+
+ DllNames - Receives a pointer to an array of UNICODE_STRINGs
+ This buffer must be freed using LsapFreePrivilegeDllNames.
+
+ DllCount - Receives the number of DLL names returned.
+
+Return Value:
+
+ STATUS_SUCCESS - The names have been successfully retrieved.
+
+ STATUS_NO_MEMORY - Not enough heap was available to return the
+ information.
+
+--*/
+
+{
+ //
+ // For the time being, just hard-code our one, known privilege DLL
+ // name as a return value.
+
+ (*DllCount) = 1;
+
+ MsDllName.Length = 14;
+ MsDllName.MaximumLength = 14;
+ MsDllName.Buffer = &MsDllNameString[0];
+
+ (*DllNames) = &MsDllName;
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+NTSTATUS
+LsapValidatePrivilegeDlls()
+
+/*++
+
+Routine Description:
+
+ This routine walks each loaded privilege DLL and validates
+ its structure. It also collects some information from each
+ DLL for later use.
+
+ Any DLLs found to have an invalid structure or revision are
+ discarded and an error log entry is made describing the
+ problem.
+
+
+
+Arguments:
+
+ None - This operates off the global privilege dll data.
+
+Return Value:
+
+ STATUS_SUCCESS - The DLLs have been validated.
+
+
+--*/
+{
+
+ //
+ //
+ // !!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!
+ //
+ // Before supported user loadable privilege DLLs, we must add
+ // code here to validate the structure of the loaded DLL. This
+ // is necessary to prevent an invalid privilege DLL structure
+ // from causing us to crash. Mostly, make sure all the lengths
+ // fall within the DLL. The SizeofResource() routine will be
+ // usefull for this.
+ //
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //
+
+ NTSTATUS Status;
+ ULONG i, RemainingCount, CurrentIndex;
+ HANDLE InfoResource, InfoLoad, InfoLock;
+ PWORD NextWord;
+ WORD MajorLanguage, MinorLanguage;
+ BOOLEAN Discard;
+
+
+
+//DbgPrint("Checking validity of %ld DLLS...\n", (ULONG)LsapPrivilegeDllCount);
+
+ RemainingCount = LsapPrivilegeDllCount;
+ CurrentIndex = 0;
+ for ( i=0; i<RemainingCount; i++) {
+
+ Discard = TRUE;
+
+ //
+ // Check the revision levels, get the privilege count,
+ // and get the default language.
+ //
+
+
+ InfoResource = FindResourceEx(
+ LsapPrivilegeDlls[ CurrentIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_DLL_INFO),
+ (WORD)LsapNeutralEnglish
+ );
+ if (InfoResource != NULL) {
+
+ InfoLoad = LoadResource(
+ LsapPrivilegeDlls[ CurrentIndex ].DllHandle,
+ InfoResource
+ );
+ if (InfoLoad != NULL) {
+
+ InfoLock = LockResource(InfoLoad);
+
+ if (InfoLock != NULL) {
+ NextWord = (PWORD)InfoLock;
+
+ //
+ // First word is major revision.
+ //
+
+ if ((*NextWord++) == LSA_PRIVILEGE_DLL_MAJOR_REV_1) {
+ if ((*NextWord++) == LSA_PRIVILEGE_DLL_MINOR_REV_0) {
+ Discard = FALSE;
+ }
+ }
+ }
+ }
+ }
+
+
+
+ if (Discard == FALSE) {
+
+ //
+ // Now get the privilege count and default language.
+ //
+
+ MajorLanguage = (*NextWord++);
+ MinorLanguage = (*NextWord++);
+ LsapPrivilegeDlls[ CurrentIndex ].Language =
+ MAKELANGID( MajorLanguage, MinorLanguage);
+
+ LsapPrivilegeDlls[ CurrentIndex ].Count = (*NextWord++);
+//DbgPrint(" DLL[%ld]:\n", i);
+//DbgPrint(" PrivilegeCount = %ld\n",(ULONG)LsapPrivilegeDlls[ CurrentIndex ].Count);
+//DbgPrint(" Language = 0x%lx\n",(ULONG)LsapPrivilegeDlls[ CurrentIndex ].Language);
+
+
+
+ Discard = FALSE; //Default for next segment of code
+
+ //
+ // Walk each table in the DLL making sure their lengths don't exceed
+ // the range of the DLL.
+ //
+
+ Status = LsapValidateProgrammaticNames( CurrentIndex );
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Make sure one of the languages present is the default language.
+ //
+
+ //FIX, FIX - do this before allowing user-produced DLLs.
+ // This is NOT a product 1 required fix.
+ // How do we know which languages have been loaded
+ // into this DLL? This is NOT a product 1 required fix.
+
+ Discard = FALSE;
+
+ }
+
+
+ }
+
+
+ if (Discard == TRUE) {
+
+#if DBG
+ DbgPrint("\nLSASS: Discarding privilege display name DLL[%ld].\n\n", i);
+#endif
+ //
+ // Discard it.
+ //
+
+ LsapPrivilegeDlls[CurrentIndex] =
+ LsapPrivilegeDlls[LsapPrivilegeDllCount];
+ LsapPrivilegeDllCount--;
+
+ //
+ // LOG AN ERROR - This is the only way for developers to determine
+ // why their privileges didn't load. Should log
+ // an error when we discovered the problem too,
+ // giving as clear a description of the problem
+ // as we can. Again, this is not a product 1
+ // requirement.
+ //
+
+ } else {
+
+ CurrentIndex++;
+ }
+ }
+
+
+ return(STATUS_SUCCESS);
+}
+
+
+
+NTSTATUS
+LsapValidateProgrammaticNames(
+ ULONG DllIndex
+ )
+/*++
+
+Routine Description:
+
+ This routine validates the structure of a programmatic names
+ table in a privilege dll.
+
+
+
+Arguments:
+
+ DllIndex - The index of the DLL to validate. This operates off
+ the global privilege dll data.
+
+Return Value:
+
+ STATUS_SUCCESS - The table has been validated.
+
+ STATUS_UNSUCCESSFUL - The DLL has a problem. A message will be
+ logged.
+
+
+--*/
+{
+ WORD i;
+ HANDLE ProgrammaticResource, ProgrammaticLoad, ProgrammaticLock;
+ PWORD NextWord;
+ WORD OffsetToNextEntry;
+
+
+ ProgrammaticResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_PROGRAM_NAMES),
+ (WORD)LsapNeutralEnglish
+ );
+ if (ProgrammaticResource == NULL) {
+
+#if DBG
+ DbgPrint("\n");
+ DbgPrint("Lsa Server: Can't find programmatic name table in privilege\n");
+ DbgPrint(" DLL. DLL index = %ld\n\n", DllIndex);
+#endif //DBG
+
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+
+ ProgrammaticLoad = LoadResource(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ ProgrammaticResource
+ );
+ if (ProgrammaticLoad == NULL) {
+#if DBG
+ DbgPrint("\n");
+ DbgPrint("Lsa Server: Can't load programmatic name table in privilege\n");
+ DbgPrint(" DLL. DLL index = %ld\n\n", DllIndex);
+#endif //DBG
+
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ ProgrammaticLock = LockResource(ProgrammaticLoad);
+
+ if (ProgrammaticLock == NULL) {
+#if DBG
+ DbgPrint("\n");
+ DbgPrint("Lsa Server: Can't lock programmatic name table in privilege\n");
+ DbgPrint(" DLL. DLL index = %ld\n\n", DllIndex);
+#endif //DBG
+
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ NextWord = (PWORD)ProgrammaticLock;
+
+
+
+
+ //
+ // Walk the list of defined privileges in this DLL looking for
+ // a match.
+ //
+
+ for ( i=0; i<LsapPrivilegeDlls[ DllIndex ].Count; i++) {
+
+ //
+ // Skip index
+ //
+ if (i != (*NextWord)) {
+#if DBG
+ DbgPrint("\n");
+ DbgPrint("Lsa Server: Error while processing privilege DLL: %ld\n", DllIndex);
+ DbgPrint(" Privilege Name Table Structure flaw.\n");
+ DbgPrint(" Privilege Index not found where expected. This\n");
+ DbgPrint(" is typically caused by a message length being specified\n");
+ DbgPrint(" incorrectly. Please check programmatic privilege name\n");
+ DbgPrint(" lengths at or around the definition of privilege %d\n\n",i);
+
+#endif //DBG
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ NextWord++;
+ NextWord++;
+
+ //
+ // Save offset to next entry and skip the actual string length
+ //
+
+ OffsetToNextEntry = (*NextWord++);
+ (NextWord++);
+
+ //
+ // calculate address of next index
+ //
+
+ NextWord = (PWORD)( (PUCHAR)NextWord + OffsetToNextEntry );
+
+ }
+
+ return(STATUS_SUCCESS);
+
+}
+
+
+
+NTSTATUS
+LsapBuildPrivilegeAuditString(
+ IN PPRIVILEGE_SET PrivilegeSet,
+ OUT PUNICODE_STRING ResultantString,
+ OUT PBOOLEAN FreeWhenDone
+ )
+
+/*++
+
+Routine Description:
+
+
+ This function builds a unicode string representing the specified
+ privileges. The privilege strings returned are program names.
+ These are not as human-friendly as the display names, but I don't
+ think we stand a chance of actually showing several display names
+ in an audit viewer.
+
+ if no privileges are present in the privilege set, then a string
+ containing a dash is returned.
+
+
+ !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !!
+ !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !!
+ !! WARNING WARNING !!
+ !! WARNING For performance sake, this routine modifies WARNING !!
+ !! WARNING the privilege set passed as an IN parameter. WARNING !!
+ !! WARNING It does this so that it doesn't have to walk WARNING !!
+ !! WARNING through the list of privileges twice. In the WARNING !!
+ !! WARNING first pass through the privileges (adding up WARNING !!
+ !! WARNING lengths needed) it stores the address of the WARNING !!
+ !! WARNING corresponding string in the PrivilegeSet's WARNING !!
+ !! WARNING Attributes field. WARNING !!
+ !! WARNING WARNING !!
+ !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !!
+ !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !! WARNING !!
+
+
+
+Arguments:
+
+ PrivilegeSet - points to the privilege set to be converted to string
+ format.
+
+ ResultantString - Points to the unicode string header. The body of this
+ unicode string will be set to point to the resultant output value
+ if successful. Otherwise, the Buffer field of this parameter
+ will be set to NULL.
+
+ FreeWhenDone - If TRUE, indicates that the body of ResultantString
+ must be freed to process heap when no longer needed.
+
+
+
+Return Values:
+
+ STATUS_NO_MEMORY - indicates memory could not be allocated
+ for the string body.
+
+ All other Result Codes are generated by called routines.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ USHORT LengthNeeded;
+ ULONG j;
+ ULONG i;
+
+ PLUID Privilege;
+
+ UNICODE_STRING LineFormatting;
+ UNICODE_STRING QuestionMark;
+
+ PUNICODE_STRING * PrivName;
+
+ PWSTR NextName;
+
+
+ //
+ // we are using a field in the IN PrivilegeSet parameter to
+ // avoid a second pass through the privileges. Make an assertion
+ // that the field we are using is the same size as a pointer.
+ // Note that this will not be the case in a 64-bit system, and
+ // so this code will need to be modified to run in such an environment.
+ //
+
+ ASSERT ( sizeof(ULONG) == sizeof(PULONG) );
+
+
+ RtlInitUnicodeString( &LineFormatting, L"\r\n\t\t\t");
+ RtlInitUnicodeString( &QuestionMark, L"?");
+
+
+
+ if (PrivilegeSet->PrivilegeCount == 0) {
+
+ //
+ // No privileges. Return a dash
+ //
+
+ Status = LsapAdtBuildDashString( ResultantString, FreeWhenDone );
+ return(Status);
+
+ }
+
+
+ LengthNeeded = 0;
+
+ for (j=0; j<PrivilegeSet->PrivilegeCount; j++) {
+
+ Privilege = &(PrivilegeSet->Privilege[j].Luid);
+ PrivName = ((PUNICODE_STRING *)&(PrivilegeSet->Privilege[j].Attributes));
+
+ for ( i=0; i<LsapWellKnownPrivilegeCount; i++) {
+
+ if (RtlEqualLuid(Privilege, &LsapKnownPrivilege[i].LocalValue)) {
+
+ (*PrivName) = &LsapKnownPrivilege[i].Name;
+
+ //
+ // Add in the length and the line formatting length.
+ // We'll subtract off the line formatting length for the
+ // last line.
+ //
+
+ LengthNeeded += (*PrivName)->Length +
+ LineFormatting.Length;
+
+ break;
+ }
+ }
+
+
+ //
+ // There is a possibility that there is no such privilege with
+ // the specified value. In this case, generate a "?".
+ //
+
+ if (i >= LsapWellKnownPrivilegeCount) {
+ (*PrivName) = &QuestionMark;
+ LengthNeeded += QuestionMark.Length +
+ LineFormatting.Length;
+ }
+ }
+
+ //
+ // Subtract off the length of the last line-formatting.
+ // It isn't needed for the last line.
+ // BUT! Add in enough for a null termination.
+ //
+
+ LengthNeeded = LengthNeeded -
+ LineFormatting.Length +
+ sizeof( WCHAR );
+
+
+ //
+ // We now have the length we need.
+ // Allocate the buffer and go through the list again copying names.
+ //
+
+ ResultantString->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, (ULONG)LengthNeeded);
+ if (ResultantString->Buffer == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+ ResultantString->Length = LengthNeeded - (USHORT)sizeof(UNICODE_NULL);
+ ResultantString->MaximumLength = LengthNeeded;
+
+
+ NextName = ResultantString->Buffer;
+ for (j=0; j<PrivilegeSet->PrivilegeCount; j++) {
+
+ //
+ // Copy the privilege name
+ //
+
+ PrivName = ((PUNICODE_STRING *)&(PrivilegeSet->Privilege[j].Attributes));
+ RtlCopyMemory( NextName,
+ (*PrivName)->Buffer,
+ (*PrivName)->Length
+ );
+ NextName = (PWSTR)((PCHAR)NextName + (*PrivName)->Length);
+
+ //
+ // Copy the line formatting string, unless this is the last priv.
+ //
+
+ if (j<PrivilegeSet->PrivilegeCount-1) {
+ RtlCopyMemory( NextName,
+ LineFormatting.Buffer,
+ LineFormatting.Length
+ );
+ NextName = (PWSTR)((PCHAR)NextName + LineFormatting.Length);
+ }
+
+ }
+
+ //
+ // Add a null to the end
+ //
+
+ (*NextName) = (UNICODE_NULL);
+
+
+ (*FreeWhenDone) = TRUE;
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+LsapDbInitWellKnownPrivilegeName(
+ IN ULONG Index,
+ IN PUNICODE_STRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the Name string to point to the
+ well-known privilege name specified by Index.
+
+ NOTE: This routine is a bit of a hack. It assumes that
+ we have a fixed number of privileges in the system
+ (which is true for Daytona) and that these privileges
+ are in a loaded DLL which will not be unloaded until
+ the system is shutdown. The privileges are expected
+ to be arranged in the DLL in order of their LUIDs.
+ That is, the low part of their LUID is an index into
+ the array of privileges in the DLL.
+
+
+Arguments:
+
+ Index - Index of privilege in MSPRIVS.DLL.
+
+ Name - The unicode string to be initialized.
+
+Return Value:
+
+ STATUS_SUCCESS - The privilege was found and Name is initialized.
+
+ STATUS_NO_SUCH_PRIVILEGE - There is no privilege with the specified
+ LUID.
+
+ Other values, the privilege was not found, the name has
+ been set to zero length.
+
+ All Result Codes are generated by called routines.
+--*/
+
+{
+ HANDLE
+ ProgrammaticResource,
+ ProgrammaticLoad,
+ ProgrammaticLock;
+
+ PWORD
+ NextWord;
+
+ UNICODE_STRING
+ TmpName;
+
+ WORD
+ i,
+ DllIndex = 0,
+ OffsetToNextEntry;
+
+
+
+//DbgPrint("Searching DLL[0] for privilege: [0, %d]...\n", Index);
+
+ //
+ // Prepare for failure
+ //
+
+ Name->MaximumLength = 0;
+ Name->Length = 0;
+ Name->Buffer = NULL;
+
+
+ ProgrammaticResource = FindResourceEx(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ RT_RCDATA,
+ MAKEINTRESOURCE(LSA_PRIVILEGE_PROGRAM_NAMES),
+ (WORD)LsapNeutralEnglish
+ );
+ if (ProgrammaticResource == NULL) {
+ ASSERT( NT_SUCCESS(STATUS_INTERNAL_DB_CORRUPTION) );
+ return(STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+
+ ProgrammaticLoad = LoadResource(
+ LsapPrivilegeDlls[ DllIndex ].DllHandle,
+ ProgrammaticResource
+ );
+ if (ProgrammaticLoad == NULL) {
+ ASSERT( NT_SUCCESS(STATUS_INTERNAL_DB_CORRUPTION) );
+ return(STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+ ProgrammaticLock = LockResource(ProgrammaticLoad);
+
+ if (ProgrammaticLock == NULL) {
+ ASSERT( NT_SUCCESS(STATUS_INTERNAL_DB_CORRUPTION) );
+ return(STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+ NextWord = (PWORD)ProgrammaticLock;
+
+
+
+
+ //
+ // Walk the list of defined privileges in this DLL looking for
+ // a match.
+ //
+
+ for ( i=0; i<LsapPrivilegeDlls[ DllIndex ].Count; i++) {
+
+ ASSERT( i == (*NextWord) ); // Expect this to be the index;
+
+
+ //
+ // Skip index
+ //
+
+ NextWord++;
+ NextWord++;
+
+ //
+ // Save offset to next entry and then
+ // set up a unicode string representing the privilege
+ // name. Make sure NextWord is left pointing at the
+ // beginning of the name buffer.
+ //
+
+ OffsetToNextEntry = (*NextWord);
+
+ TmpName.MaximumLength = (*NextWord++); // Skip the NextOffset field
+ TmpName.Length = (*NextWord++); // Skip the Length field
+ TmpName.Buffer = (PVOID)NextWord;
+ if ( (i+SE_MIN_WELL_KNOWN_PRIVILEGE) == (WORD)Index ) {
+ (*Name) = TmpName;
+//DbgPrint(" Assigning to *%Z*\n", Name);
+ return(STATUS_SUCCESS);
+
+ }
+
+ NextWord = (PWORD)( (PUCHAR)NextWord + OffsetToNextEntry );
+
+ }
+
+ return(STATUS_NO_SUCH_PRIVILEGE);
+
+
+
+}
diff --git a/private/lsa/server/dbsamtst.c b/private/lsa/server/dbsamtst.c
new file mode 100644
index 000000000..ef68b8201
--- /dev/null
+++ b/private/lsa/server/dbsamtst.c
@@ -0,0 +1,1062 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbsamtst.c
+
+Abstract:
+
+ LSA Database - SAM Database load testing code
+
+ When enabled, this code provides a mechanism for stress loading of the
+ SAM database with a large number of accounts. The load executes in the
+ context of lsass.exe, similar to how it does in replication.
+
+Author:
+
+ Scott Birrell (ScottBi) January 10, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include <lsaisrv.h>
+#include "dbp.h"
+
+
+//
+// Uncomment the define LSA_SAM_ACCOUNTS_DOMAIN_TEST to enable the
+// code needed for the ctsamdb test program. Recompile dbsamtst.c,
+// dbpolicy.c. rebuild lsasrv.dll and nmake UMTYPE=console UMTEST=ctsamdb.
+//
+
+#ifdef LSA_SAM_ACCOUNTS_DOMAIN_TEST
+
+//
+// Global data needed by test
+//
+
+//
+// Random Unicode text Buffer (gets updated pseudo-randomly during run)
+//
+
+#define LSAP_DB_TEST_TEXT_BUFF_CHAR_LENGTH ((ULONG) 1024)
+
+#define LSAP_DB_TEST_TEXT_BUFF_BYTE_LENGTH \
+ (LSAP_DB_TEST_TEXT_BUFF_CHAR_LENGTH * sizeof (WCHAR))
+
+static PWSTR TextBuffer = L"ABCDEFGHIJKLMNOPQRSTUVWXYZAABBCC"
+ L"DDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSS"
+ L"TTUUVVWWXXYYZZZYXWVUTSRQPONMLKJI"
+ L"HGFEDCBAMEJHCKZXHDKCXJHCKXSKLCJJ"
+ L"QKXJHDHNSLCJIFUENSGXCJCVKLSJKSHD"
+ L"QXKXJHDGHASOESDGPMSDVJKSDNJCKACJ"
+ L"AOJSDVJKJASDHABJKACSBNVAJKAJSKDV"
+ L"AJLSBSDJBVAJKLQWDQBVAKLJSCVAKLDD"
+ L"OJWQEFBNQGOTQJWDBVBJOQWDBXQPJJPB"
+ L"QWERTYUIOPASDFGHJKLZXCVBNMQUFIEJ"
+ L"ARWFUITYBNOMVJGBLJHKGJKGKGTJHKHS"
+ L"AOPDEBLFPYVQRUCVKJGRKSDBKGKWPOID"
+ L"MBCZLADJGFDOYEJHLSDYQWHLSOQLFHBF"
+ L"MCNXBZLGHKFDKDHSAQTWYEUTODMKCVJW"
+ L"QOPKIJUNYBTYVTCRCXEXECDWZQGGICRT"
+ L"ZWRFXCERYHVFYJNGHJNMBGTYVFJNMJJK";
+
+//
+// Random Unicode text Buffer backup (used for reset)
+//
+
+static PWSTR TextBuffBak = L"ABCDEFGHIJKLMNOPQRSTUVWXYZAABBCC"
+ L"DDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSS"
+ L"TTUUVVWWXXYYZZZYXWVUTSRQPONMLKJI"
+ L"HGFEDCBAMEJHCKZXHDKCXJHCKXSKLCJJ"
+ L"QKXJHDHNSLCJIFUENSGXCJCVKLSJKSHD"
+ L"QXKXJHDGHASOESDGPMSDVJKSDNJCKACJ"
+ L"AOJSDVJKJASDHABJKACSBNVAJKAJSKDV"
+ L"AJLSBSDJBVAJKLQWDQBVAKLJSCVAKLDD"
+ L"OJWQEFBNQGOTQJWDBVBJOQWDBXQPJJPB"
+ L"QWERTYUIOPASDFGHJKLZXCVBNMQUFIEJ"
+ L"ARWFUITYBNOMVJGBLJHKGJKGKGTJHKHS"
+ L"AOPDEBLFPYVQRUCVKJGRKSDBKGKWPOID"
+ L"MBCZLADJGFDOYEJHLSDYQWHLSOQLFHBF"
+ L"MCNXBZLGHKFDKDHSAQTWYEUTODMKCVJW"
+ L"QOPKIJUNYBTYVTCRCXEXECDWZQGGICRT"
+ L"ZWRFXCERYHVFYJNGHJNMBGTYVFJNMJJK";
+
+static ULONG Array0Index = 0,
+ Array1Index = 0,
+ Array2Index = 0,
+ Array3Index = 0,
+ Array4Index = 0,
+ Array5Index = 0;
+
+
+static ULONG Array0[] = { 0, 44, 88 };
+static ULONG Array1[] = { 87,2,45,48,68};
+static ULONG Array2[] = { 23,1,43,37,4,16,8 };
+static ULONG Array3[] = { 64,5,17,29,18,17,22,56,19,20,6 };
+static ULONG Array4[] = { 7,11,97,88,8,5,24,0,99,51,33,20,80 };
+static ULONG Array5[] = { 53,56,3,27,29,98,35,47,53,92,89,4,66,34,1,99,3 };
+
+
+NTSTATUS
+LsapDbTestLoadSamAccountsDomain(
+ IN PUNICODE_STRING NumberOfAccounts
+ );
+
+NTSTATUS
+LsapDbTestLoadSamAccountsDomainInitialize(
+ IN OUT PUSER_ALL_INFORMATION UserInformation
+ );
+
+NTSTATUS
+LsapDbTestCreateNextAccountInfo(
+ IN OUT PUSER_ALL_INFORMATION UserInformation
+ );
+
+NTSTATUS
+LsapDbTestGenUnicodeString(
+ OUT PUNICODE_STRING OutputString,
+ IN ULONG MinimumLength,
+ IN ULONG MaximumLength
+ );
+
+PWSTR
+LsapDbTestGenRandomStringPointer(
+ IN ULONG MinLengthToLeaveAtEnd
+ );
+
+ULONG
+LsapDbTestGenRandomNumber(
+ IN ULONG MinimumValue,
+ IN ULONG MaximumValue
+ );
+
+NTSTATUS
+LsapDbTestLoadSamAccountsDomain(
+ IN PUNICODE_STRING NumberOfAccounts
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a number of users in the local SAM Accounts Domain,
+ or deletes previously created users in the domain.
+
+Arguments:
+
+ AccountCount - Specifies the number of accounts to be created. If a negative
+ number is specified, the accounts are deleted.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status, EnumerateStatus;
+ LONG AccountNumber = 0;
+ LONG AccountCount = 0;
+ LONG CollisionCount = 0;
+ LONG ExistingAccountCount = 0;
+ LONG DeletedUserCount = 0;
+ ANSI_STRING NumberOfAccountsAnsi;
+ PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
+ LSA_TRUST_INFORMATION TrustInformation;
+ SAMPR_HANDLE LocalSamServerHandle = NULL;
+ USER_ALL_INFORMATION UserInformation;
+ SAMPR_HANDLE UserHandle = NULL;
+ SAMPR_HANDLE LocalSamDomainHandle = NULL;
+ ULONG InitialRelativeId, RelativeId;
+ ULONG CountReturned;
+ SAM_ENUMERATE_HANDLE EnumerationContext = 0;
+ PSAMPR_ENUMERATION_BUFFER RidEnumerationBuffer = NULL;
+ ULONG NextAccount;
+ ULONG ConflictingAccountRid;
+ LARGE_INTEGER StartTime, TimeAfterThis100Users, TimeAfterPrevious100Users;
+ LARGE_INTEGER TimeForThis100Users, TimeForThis100UsersInMs, TotalTime, TenThousand;
+
+
+ //
+ // Open the local SAM Accounts Domain
+ //
+ // The Sid and Name of the Account Domain are both configurable, and
+ // we need to obtain them from the Policy Object. Now obtain the
+ // Account Domain Sid and Name by querying the appropriate
+ // Policy Information Class.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ LsapPolicyHandle,
+ PolicyAccountDomainInformation,
+ (PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsarQueryInformationPolicy failed 0x%lx\n", Status));
+ goto TestLoadSamAccountsDomainError;
+ }
+
+ //
+ // Set up the Trust Information structure for the Account Domain.
+ //
+
+ TrustInformation.Sid = PolicyAccountDomainInfo->DomainSid;
+
+ RtlCopyMemory(
+ &TrustInformation.Name,
+ &PolicyAccountDomainInfo->DomainName,
+ sizeof (UNICODE_STRING)
+ );
+
+ //
+ // Connect to the Local Sam. The LSA server is a trusted client, so we
+ // call the internal version of the SamConnect routine.
+ //
+
+ Status = SamIConnect( NULL, &LocalSamServerHandle, 0, TRUE );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("SamIConnect failed 0x%lx\n", Status));
+ goto TestLoadSamAccountsDomainError;
+ }
+
+ //
+ // Open the Account Domain.
+ //
+
+ Status = SamrOpenDomain(
+ LocalSamServerHandle,
+ DOMAIN_LOOKUP,
+ TrustInformation.Sid,
+ &LocalSamDomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("SamrOpenDomain failed 0x%lx\n", Status));
+ goto TestLoadSamAccountsDomainError;
+ }
+
+ //
+ // Now convert the count of accounts from unicode.
+ //
+
+ Status = RtlUnicodeStringToAnsiString(
+ &NumberOfAccountsAnsi,
+ NumberOfAccounts,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("RtlUnicodeStringToAnsiString failed 0x%lx\n", Status));
+ goto TestLoadSamAccountsDomainError;
+ }
+
+ AccountCount = (LONG) atoi(NumberOfAccountsAnsi.Buffer);
+
+ //
+ // Tke initial relative id is used by both the create and delete
+ // account branches so initialize it here.
+ //
+
+ InitialRelativeId = (ULONG) 0x00001000L;
+
+ //
+ // If the number of accounts is > 0, add the specified number of
+ // accounts to the SAM account domain. If the number of accounts is
+ // < 0, delete all accounts from the SAM account s domain.
+ //
+
+ if (AccountCount > 0) {
+
+ //
+ // Initialize the constant fields in the UserInformation structure
+ //
+
+ Status = LsapDbTestLoadSamAccountsDomainInitialize(&UserInformation);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto TestLoadSamAccountsDomainError;
+ }
+
+ KdPrint(("Sam Account Database Load Begins\n"));
+
+ Status = NtQuerySystemTime( &StartTime );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ TimeAfterPrevious100Users = StartTime;
+
+ //
+ // Conversion factor for converting 100ns ticks to milliseconds
+ //
+
+ TenThousand = RtlConvertUlongToLargeInteger((ULONG) 10000);
+
+ for( AccountNumber = 0; AccountNumber < AccountCount; AccountNumber++) {
+
+ //
+ // Generate Information for this Account
+ //
+
+ Status = LsapDbTestCreateNextAccountInfo(&UserInformation);
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ RelativeId = InitialRelativeId + (ULONG) AccountNumber;
+
+ //
+ // Create the account
+ //
+
+ ConflictingAccountRid = (ULONG) 0;
+
+ Status = SamICreateAccountByRid(
+ LocalSamDomainHandle,
+ SamObjectUser,
+ RelativeId,
+ (PRPC_UNICODE_STRING) &UserInformation.UserName,
+ USER_ALL_ACCESS,
+ &UserHandle,
+ &ConflictingAccountRid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if ((Status == STATUS_USER_EXISTS) || (Status == STATUS_OBJECT_TYPE_MISMATCH)) {
+
+ CollisionCount++;
+ AccountNumber--;
+ if ((CollisionCount%100)==0) {
+ KdPrint(("."));
+ }
+ continue;
+ }
+
+ KdPrint(("SamrCreateAccountByRid failed 0x%lx\n", Status));
+ break;
+ }
+
+ //
+ // If an account already existed with the given Rid and had
+ // the same account type and name, it will have been opened.
+ // Record the number of occurrences of this.
+ //
+
+ if (ConflictingAccountRid == RelativeId) {
+
+ ExistingAccountCount++;
+ }
+
+ //
+ // Set the information for the account
+ //
+
+ UserInformation.UserId = RelativeId;
+
+ Status = SamrSetInformationUser(
+ UserHandle,
+ UserAllInformation,
+ (PSAMPR_USER_INFO_BUFFER) &UserInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("SamrSetInformationUser failed 0x%lx\n", Status));
+ break;
+ }
+
+ Status = SamrCloseHandle( &UserHandle );
+
+ if ((AccountNumber > 0) && ((AccountNumber % 100) == 0)) {
+
+ Status = NtQuerySystemTime( &TimeAfterThis100Users );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ TimeForThis100Users.QuadPart = TimeAfterThis100Users.QuadPart -
+ TimeAfterPrevious100Users.QuadPart;
+
+ TimeForThis100UsersInMs = TimeForThis100Users.QuadPart /
+ TenThousand.QuadPart;
+
+ TotalTime = TimeAfterThis100Users.QuadPart -
+ StartTime.QuadPart;
+
+ KdPrint(("%d Accounts Created\n", AccountNumber));
+ KdPrint(("Last 100 users took %d millisecs to create\n", TimeForThis100UsersInMs.LowPart));
+
+ if (ExistingAccountCount > 0) {
+
+ KdPrint(("%d Attempts to create non-conflicting existing users\n\n", ExistingAccountCount));
+ }
+
+ if (CollisionCount > 0) {
+
+ KdPrint(("%d Creation conflicts with existing users\n", CollisionCount));
+ }
+
+ TimeAfterPrevious100Users = TimeAfterThis100Users;
+ }
+ }
+
+ KdPrint(("%d Accounts Created\n", AccountNumber));
+ KdPrint(("%d existing accounts opened\n", ExistingAccountCount));
+ KdPrint(("%d Rid/Name/Type conflicts with existing accounts\n", CollisionCount));
+ KdPrint(("\nSam Account Database Load Ends\n"));
+
+ } else if (AccountCount < 0) {
+
+ //
+ // Delete the accounts
+ //
+
+ KdPrint(("Deleting user accounts with Rid >= 4096 in the local SAM Accounts Domain\n"));
+
+ EnumerateStatus = STATUS_MORE_ENTRIES;
+
+ while(EnumerateStatus == STATUS_MORE_ENTRIES) {
+
+ RidEnumerationBuffer = NULL;
+
+ Status = SamrEnumerateUsersInDomain(
+ LocalSamDomainHandle,
+ &EnumerationContext,
+ USER_NORMAL_ACCOUNT,
+ (PSAMPR_ENUMERATION_BUFFER *) &RidEnumerationBuffer,
+ 4096,
+ &CountReturned
+ );
+
+ EnumerateStatus = Status;
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("SamrEnumerateUsersInDomain failed 0x%lx\n", Status));
+ break;
+ }
+
+ if (CountReturned == 0) {
+
+ break;
+ }
+
+ for (NextAccount = 0; NextAccount < CountReturned; NextAccount++) {
+
+ RelativeId = RidEnumerationBuffer->Buffer[ NextAccount ].RelativeId;
+
+ //
+ // Skip this account if it's not one of ours. This is not
+ // a rigorous check, but works.
+ //
+
+ if (RelativeId < InitialRelativeId) {
+
+ continue;
+ }
+
+ Status = SamrOpenUser(
+ LocalSamDomainHandle,
+ DELETE,
+ RelativeId,
+ &UserHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("SamrOpenUser failed 0x%lx\n", Status));
+ break;
+ }
+
+ Status = SamrDeleteUser( &UserHandle );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_SPECIAL_ACCOUNT) {
+
+ KdPrint(("SamrDeleteUser failed 0x%lx\n", Status));
+ break;
+ }
+
+ Status = SamrCloseHandle(&UserHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("SamrCloseHandle failed 0x%lx\n", Status));
+ break;
+ }
+ }
+
+ DeletedUserCount++;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ KdPrint(("%d user accounts deleted\n", DeletedUserCount));
+ KdPrint(("Deletion of user accounts ends\n"));
+ }
+ }
+
+TestLoadSamAccountsDomainFinish:
+
+ return(Status);
+
+TestLoadSamAccountsDomainError:
+
+ goto TestLoadSamAccountsDomainFinish;
+}
+
+
+NTSTATUS
+LsapDbTestLoadSamAccountsDomainInitialize(
+ IN OUT PUSER_ALL_INFORMATION UserInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the constant values required by the
+ SAM Accounts Domain load/update tests. These values include
+ static global data and fixed values within a USER_ALL_INFORMATION
+ structure used for creating/updating accounts.
+
+Arguments:
+
+ UserInformation - Points to USER_ALL_INFORMATION structure.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Index;
+
+ static UCHAR LogonHours[SAM_HOURS_PER_WEEK / 8];
+
+ //
+ // Restore the Unicode Text Buffer used for generating random
+ // Unicode Strings to its original contents.
+ //
+
+ RtlCopyMemory( TextBuffer, TextBuffBak, LSAP_DB_TEST_TEXT_BUFF_CHAR_LENGTH );
+
+ //
+ // Reset the random number generator indices
+ //
+
+ Array0Index = 0;
+ Array1Index = 0;
+ Array2Index = 0;
+ Array3Index = 0;
+ Array4Index = 0;
+ Array5Index = 0;
+
+ //
+ // Initialize Logon Hours data
+ //
+
+ for( Index = 0; Index < (SAM_HOURS_PER_WEEK / 8); Index++) {
+
+ LogonHours[Index] = (UCHAR) 0xff;
+ }
+
+ //
+ // Initialize constaint values in User Information structure
+ //
+
+ UserInformation->LogonCount = 0;
+ UserInformation->LogonHours.UnitsPerWeek = SAM_HOURS_PER_WEEK;
+ UserInformation->LogonHours.LogonHours = LogonHours;
+ UserInformation->BadPasswordCount = 1;
+ UserInformation->LogonCount = 1;
+ UserInformation->UserAccountControl = USER_NORMAL_ACCOUNT;
+ UserInformation->CountryCode = 1;
+ UserInformation->CodePage = 850;
+ UserInformation->NtPasswordPresent = FALSE;
+ UserInformation->LmPasswordPresent = FALSE;
+ UserInformation->PasswordExpired = FALSE;
+ UserInformation->SecurityDescriptor.SecurityDescriptor = NULL;
+ UserInformation->SecurityDescriptor.Length = 0;
+ UserInformation->WhichFields =
+ (
+ USER_ALL_USERNAME |
+ USER_ALL_FULLNAME |
+ USER_ALL_ADMINCOMMENT |
+ USER_ALL_USERCOMMENT |
+ USER_ALL_HOMEDIRECTORY |
+ USER_ALL_HOMEDIRECTORYDRIVE |
+ USER_ALL_SCRIPTPATH |
+ USER_ALL_PROFILEPATH |
+ USER_ALL_LOGONHOURS |
+ USER_ALL_USERACCOUNTCONTROL
+ );
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbTestCreateNextAccountInfo(
+ IN OUT PUSER_ALL_INFORMATION UserInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates the user information for the next user.
+
+Arguments:
+
+ UserInformation - Pointer to User Information structure
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+
+#define LSAP_TEST_MIN_USERNAME_LENGTH ((ULONG) 0x00000005L)
+#define LSAP_TEST_MAX_USERNAME_LENGTH ((ULONG) 0x0000000cL)
+
+#define LSAP_TEST_MIN_FULLNAME_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_FULLNAME_LENGTH ((ULONG) 0x00000030L)
+
+#define LSAP_TEST_MIN_HOMEDIR_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_HOMEDIR_LENGTH ((ULONG) 0x00000030L)
+
+#define LSAP_TEST_MIN_SCRIPTPATH_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_SCRIPTPATH_LENGTH ((ULONG) 0x00000100L)
+
+#define LSAP_TEST_MIN_PROFPATH_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_PROFPATH_LENGTH ((ULONG) 0x00000100L)
+
+#define LSAP_TEST_MIN_DRIVE_LENGTH ((ULONG) 0x00000001L)
+#define LSAP_TEST_MAX_DRIVE_LENGTH ((ULONG) 0x00000001L)
+
+#define LSAP_TEST_MIN_ADMCMT_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_ADMCMT_LENGTH ((ULONG) 0x00000100L)
+
+#define LSAP_TEST_MIN_WKSTA_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_WKSTA_LENGTH ((ULONG) 0x00000040L)
+
+#define LSAP_TEST_MIN_USER_COMMENT_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_USER_COMMENT_LENGTH ((ULONG) 0x00000040L)
+
+#define LSAP_TEST_MIN_PARAMS_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_PARAMS_LENGTH ((ULONG) 0x00000040L)
+
+#define LSAP_TEST_MIN_PRIVDATA_LENGTH ((ULONG) 0x00000003L)
+#define LSAP_TEST_MAX_PRIVDATA_LENGTH ((ULONG) 0x00000040L)
+
+ NTSTATUS Status;
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->UserName,
+ LSAP_TEST_MIN_USERNAME_LENGTH,
+ LSAP_TEST_MAX_USERNAME_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->FullName,
+ LSAP_TEST_MIN_FULLNAME_LENGTH,
+ LSAP_TEST_MAX_FULLNAME_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->HomeDirectoryDrive,
+ LSAP_TEST_MIN_DRIVE_LENGTH,
+ LSAP_TEST_MAX_DRIVE_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->HomeDirectory,
+ LSAP_TEST_MIN_HOMEDIR_LENGTH,
+ LSAP_TEST_MAX_HOMEDIR_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->ScriptPath,
+ LSAP_TEST_MIN_SCRIPTPATH_LENGTH,
+ LSAP_TEST_MAX_SCRIPTPATH_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->ProfilePath,
+ LSAP_TEST_MIN_PROFPATH_LENGTH,
+ LSAP_TEST_MAX_PROFPATH_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->AdminComment,
+ LSAP_TEST_MIN_ADMCMT_LENGTH,
+ LSAP_TEST_MAX_ADMCMT_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->WorkStations,
+ LSAP_TEST_MIN_WKSTA_LENGTH,
+ LSAP_TEST_MAX_WKSTA_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->UserComment,
+ LSAP_TEST_MIN_USER_COMMENT_LENGTH,
+ LSAP_TEST_MAX_USER_COMMENT_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ &UserInformation->Parameters,
+ LSAP_TEST_MIN_PARAMS_LENGTH,
+ LSAP_TEST_MAX_PARAMS_LENGTH
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbTestGenUnicodeString(
+ (PUNICODE_STRING) &UserInformation->PrivateData,
+ LSAP_TEST_MIN_PRIVDATA_LENGTH,
+ LSAP_TEST_MAX_PRIVDATA_LENGTH
+ );
+
+ return(Status);
+}
+
+NTSTATUS
+LsapDbTestGenUnicodeString(
+ OUT PUNICODE_STRING OutputString,
+ IN ULONG MinimumLength,
+ IN ULONG MaximumLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates a pseudo-random Unicode String whose length
+ lies within the indicated bounds.
+
+Arguments:
+
+ OutputString - Points to output UNICODE_STRING structure to be
+ initialized.
+
+ MinimumLength - Minimum length required in wide characters.
+
+ MaximumLength - Maximum length required in wide characters.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ OutputString->Length = (USHORT) (sizeof(WCHAR) * LsapDbTestGenRandomNumber(
+ MinimumLength,
+ MaximumLength
+ ));
+
+ OutputString->Buffer = LsapDbTestGenRandomStringPointer(
+ (OutputString->Length / 2)
+ );
+
+ OutputString->MaximumLength = OutputString->Length;
+
+ return(Status);
+}
+
+PWSTR
+LsapDbTestGenRandomStringPointer(
+ IN ULONG MinLengthToLeaveAtEnd
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns a random pointer to a location within a global
+ block of text.
+
+Arguments:
+
+ MinLengthToLeaveAtEnd - Specifies the minimum length of the string
+ in Wide Chars from the returned pointer to the end of the global buffer.
+
+Return Values:
+
+ PUCHAR - Receives a pointer referencing a character within the block
+ of text.
+
+--*/
+
+{
+ WCHAR Temp;
+
+ ULONG Offset = LsapDbTestGenRandomNumber(
+ 0,
+ (ULONG) 512 - MinLengthToLeaveAtEnd
+ );
+
+ ULONG Offset1 = LsapDbTestGenRandomNumber(
+ 0,
+ (ULONG) 512 - 1
+ );
+
+ ULONG Offset2 = LsapDbTestGenRandomNumber(
+ 0,
+ (ULONG) 512 - 1
+ );
+
+ ULONG Offset3 = LsapDbTestGenRandomNumber(
+ 0,
+ (ULONG) 512 - 1
+ );
+
+ ULONG Offset4 = LsapDbTestGenRandomNumber(
+ 0,
+ (ULONG) 512 - 1
+ );
+
+ //
+ // Randomly alter the 2nd to 6th characters of the selected string.
+ //
+
+ Temp = TextBuffer[Offset + 1];
+ TextBuffer[Offset + 1] = TextBuffer[Offset1];
+ TextBuffer[Offset1] = Temp;
+
+ if ((Offset > 0) && (Offset < (ULONG) (512 - 2))) {
+
+ Temp = TextBuffer[Offset + 2];
+ TextBuffer[Offset + 2] = TextBuffer[Offset2];
+ TextBuffer[Offset2] = Temp;
+ }
+
+ if ((Offset > 0) && (Offset < (ULONG) (512 - 3))) {
+
+ Temp = TextBuffer[Offset + 3];
+ TextBuffer[Offset + 3] = TextBuffer[Offset3];
+ TextBuffer[Offset3] = Temp;
+ }
+
+ if ((Offset > 0) && (Offset < (ULONG) (512 - 4))) {
+
+ Temp = TextBuffer[Offset + 4];
+ TextBuffer[Offset + 4] = TextBuffer[Offset4];
+ TextBuffer[Offset4] = Temp;
+ }
+
+ return(&TextBuffer[Offset]);
+}
+
+
+ULONG
+LsapDbTestGenRandomNumber(
+ IN ULONG MinimumValue,
+ IN ULONG MaximumValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates a pseudo-random unsigned integer lying between
+ two values.
+
+Arguments:
+
+ MinimumValue - The minimum value of the random number
+
+ MaximumValue - The maximum value of the random number
+
+--*/
+
+{
+ ULONG NextValue, NextValue1, NextValue2;
+ ULONG MaxPossValue = 88 + 87 + 43 + 64 + 99 + 99;
+
+ Array0Index++;
+ if (Array0Index == sizeof(Array0) / sizeof(ULONG)) {
+
+ Array0Index = 0;
+ }
+
+ Array1Index++;
+ if (Array1Index == sizeof(Array1) / sizeof(ULONG)) {
+
+ Array1Index = 0;
+ }
+
+ Array2Index++;
+ if (Array2Index == sizeof(Array2) / sizeof(ULONG)) {
+
+ Array2Index = 0;
+ }
+
+ Array3Index++;
+ if (Array3Index == sizeof(Array3) / sizeof(ULONG)) {
+
+ Array3Index = 0;
+ }
+
+ Array4Index++;
+ if (Array4Index == sizeof(Array4) / sizeof(ULONG)) {
+
+ Array4Index = 0;
+ }
+
+ Array5Index++;
+ if (Array5Index == sizeof(Array5) / sizeof(ULONG)) {
+
+ Array5Index = 0;
+ }
+
+ NextValue1 = Array0[Array0Index] +
+ Array1[Array1Index] +
+ Array2[Array2Index] +
+ Array3[Array3Index] +
+ Array4[Array4Index] +
+ Array5[Array5Index];
+
+ Array0Index++;
+ if (Array0Index == sizeof(Array0) / sizeof(ULONG)) {
+
+ Array0Index = 0;
+ }
+
+ Array1Index++;
+ if (Array1Index == sizeof(Array1) / sizeof(ULONG)) {
+
+ Array1Index = 0;
+ }
+
+ Array2Index++;
+ if (Array2Index == sizeof(Array2) / sizeof(ULONG)) {
+
+ Array2Index = 0;
+ }
+
+ Array3Index++;
+ if (Array3Index == sizeof(Array3) / sizeof(ULONG)) {
+
+ Array3Index = 0;
+ }
+
+ Array4Index++;
+ if (Array4Index == sizeof(Array4) / sizeof(ULONG)) {
+
+ Array4Index = 0;
+ }
+
+ Array5Index++;
+ if (Array5Index == sizeof(Array5) / sizeof(ULONG)) {
+
+ Array5Index = 0;
+ }
+
+ NextValue2 = Array0[Array0Index] +
+ Array1[Array1Index] +
+ Array2[Array2Index] +
+ Array3[Array3Index] +
+ Array4[Array4Index] +
+ Array5[Array5Index];
+
+ if (NextValue2 > NextValue1) {
+
+ NextValue = NextValue2 - NextValue1;
+
+ } else {
+
+ NextValue = NextValue1 - NextValue2;
+ }
+
+ NextValue = MinimumValue +
+ (MaximumValue - MinimumValue) * NextValue / MaxPossValue;
+
+ return(NextValue);
+}
+
+#endif // LSA_SAM_ACCOUNTS_DOMAIN_TEST
diff --git a/private/lsa/server/dbsecret.c b/private/lsa/server/dbsecret.c
new file mode 100644
index 000000000..5058efda9
--- /dev/null
+++ b/private/lsa/server/dbsecret.c
@@ -0,0 +1,1750 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ dbsecret.c
+
+Abstract:
+
+ LSA - Database - Secret Object Private API Workers
+
+ NOTE: This module should remain as portable code that is independent
+ of the implementation of the LSA Database. As such, it is
+ permitted to use only the exported LSA Database interfaces
+ contained in db.h and NOT the private implementation
+ dependent functions in dbp.h.
+
+Author:
+
+ Scott Birrell (ScottBi) December 12, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+
+
+NTSTATUS
+LsarCreateSecret(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_UNICODE_STRING SecretName,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE SecretHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaCreateSecret API.
+
+ The LsaCreateSecret API creates a named Secret object in the
+ Lsa Database. Each Secret Object can have two values assigned,
+ called the Current Value and the Old Value. The meaning of these
+ values is known to the Secret object creator. The caller must have
+ LSA_CREATE_SECRET access to the LsaDatabase object.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ SecretName - Pointer to Unicode String specifying the name of the
+ secret.
+
+ DesiredAccess - Specifies the accesses to be granted to the newly
+ created and opened secret.
+
+ SecretHandle - Receives a handle to the newly created and opened
+ Secret object. This handle is used on subsequent accesses to
+ the object until closed.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_COLLISION - A Secret object having the given name
+ already exists.
+
+ STATUS_TOO_MANY_SECRETS - The maximum number of Secret objects in the
+ system has been reached.
+
+ STATUS_NAME_TOO_LONG - The name of the secret is too long to be stored
+ in the LSA database.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ BOOLEAN ContainerReferenced = FALSE;
+ LSAP_DB_ATTRIBUTE Attributes[2];
+ ULONG TypeSpecificAttributeCount;
+ LARGE_INTEGER CreationTime;
+ ULONG Index;
+ ULONG CreateOptions = (ULONG) 0;
+ ULONG ReferenceOptions = (ULONG) LSAP_DB_ACQUIRE_LOCK;
+ ULONG DereferenceOptions = (ULONG) LSAP_DB_RELEASE_LOCK;
+ BOOLEAN GlobalSecret = FALSE;
+
+
+ //
+ // Check to see if the lenght of the name is within the limits of the
+ // LSA database.
+ //
+
+ if ( SecretName->Length > LSAP_DB_LOGICAL_NAME_MAX_LENGTH ) {
+ return(STATUS_NAME_TOO_LONG);
+ }
+
+ //
+ // Check for Local Secret creation request. If the Secret name does
+ // not begin with the Global Secret Prefix, the Secret is local. In
+ // this case, creation of the secret is allowed on BDC's as well as
+ // PDC's and Workstations. Creation of Global Secrets is not
+ // allowed on BDC's except for trusted callers such as a Replicator.
+ //
+
+ Status = LsapDbGetScopeSecret( SecretName, &GlobalSecret );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSecretError;
+ }
+
+ if (!GlobalSecret) {
+
+ CreateOptions |= (LSAP_DB_OMIT_REPLICATOR_NOTIFICATION |
+ LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK);
+ ReferenceOptions |= (LSAP_DB_OMIT_REPLICATOR_NOTIFICATION |
+ LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK);
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle
+ // (container object handle) is valid, is of the expected type and has
+ // all of the desired accesses granted. Reference the container
+ // object handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_CREATE_SECRET,
+ PolicyObject,
+ ReferenceOptions
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSecretError;
+ }
+
+ ContainerReferenced = TRUE;
+
+ //
+ // Fill in the ObjectInformation structure. Initialize the
+ // embedded Object Attributes with the PolicyHandle as the
+ // Root Directory (Container Object) handle and the Logical Name
+ // of the account. Store the types of the object and its container.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ (PUNICODE_STRING)SecretName,
+ OBJ_CASE_INSENSITIVE,
+ PolicyHandle,
+ NULL
+ );
+
+ ObjectInformation.ObjectTypeId = SecretObject;
+ ObjectInformation.ContainerTypeId = PolicyObject;
+ ObjectInformation.Sid = NULL;
+
+ //
+ // Set up the Creation Time as a Type Specific Attribute.
+ //
+
+ Status = NtQuerySystemTime(&CreationTime);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSecretError;
+ }
+
+ Index = 0;
+
+ Attributes[Index].AttributeName = &LsapDbNames[CupdTime];
+ Attributes[Index].AttributeValue = &CreationTime;
+ Attributes[Index].AttributeValueLength = sizeof (LARGE_INTEGER);
+ Index++;
+
+ Attributes[Index].AttributeName = &LsapDbNames[OupdTime];
+ Attributes[Index].AttributeValue = &CreationTime;
+ Attributes[Index].AttributeValueLength = sizeof (LARGE_INTEGER);
+ Index++;
+
+ TypeSpecificAttributeCount = Index;
+
+ //
+ // Create the Secret Object. We fail if the object already exists.
+ // Note that the object create routine performs a Database transaction.
+ //
+
+ Status = LsapDbCreateObject(
+ &ObjectInformation,
+ DesiredAccess,
+ LSAP_DB_OBJECT_CREATE,
+ CreateOptions,
+ Attributes,
+ TypeSpecificAttributeCount,
+ SecretHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CreateSecretError;
+ }
+
+CreateSecretFinish:
+
+ //
+ // If necessary, release the LSA Database lock.
+ //
+
+ if (ContainerReferenced) {
+
+ LsapDbReleaseLock();
+ }
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (*SecretHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+ return( Status );
+
+CreateSecretError:
+
+ //
+ // If necessary, dereference the Container Object.
+ //
+
+ if (ContainerReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ DereferenceOptions,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+ ContainerReferenced = FALSE;
+ }
+
+ goto CreateSecretFinish;
+
+}
+
+
+NTSTATUS
+LsarOpenSecret(
+ IN LSAPR_HANDLE ConnectHandle,
+ IN PLSAPR_UNICODE_STRING SecretName,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSAPR_HANDLE SecretHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the LsaOpenSecret
+ API.
+
+ The LsaOpenSecret API opens a Secret Object within the LSA Database.
+ A handle is returned which must be used to perform operations on the
+ secret object.
+
+Arguments:
+
+ ConnectHandle - Handle from an LsaOpenLsa call.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the secret object being opened. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target secret object to determine whether the accesses will be
+ granted or denied.
+
+ SecretName - Pointer to a Unicode String structure that references the
+ name of the Secret object to be opened.
+
+ SecretHandle - Pointer to location that will receive a handle to the
+ newly opened Secret object.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
+ target system's LSA Database having the specified SecretName.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_OBJECT_INFORMATION ObjectInformation;
+ BOOLEAN ContainerReferenced = FALSE;
+ BOOLEAN AcquiredLock = FALSE;
+ BOOLEAN GlobalSecret = FALSE;
+ ULONG OpenOptions = 0;
+ ULONG ReferenceOptions = LSAP_DB_ACQUIRE_LOCK;
+
+ //
+ // Check for Local Secret open request. If the Secret name does
+ // not begin with the Global Secret Prefix, the Secret is local. In
+ // this case, update/deletion of the secret is allowed on BDC's as well as
+ // PDC's and Workstations. Update/deletion of Global Secrets is not
+ // allowed on BDC's except for trusted callers such as a Replicator.
+ // To facilitate validation of the open request on BDC's we record
+ // that the BDC check should be omitted on the container reference, and
+ // that the replicator notification should be omitted on a commit.
+ //
+
+ Status = LsapDbGetScopeSecret( SecretName, &GlobalSecret );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenSecretError;
+ }
+
+ if (!GlobalSecret) {
+
+ OpenOptions |= (LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK |
+ LSAP_DB_OMIT_REPLICATOR_NOTIFICATION );
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle
+ // (container object handle) is valid, and is of the expected type.
+ // Reference the container object handle. This reference remains in
+ // effect until the child object handle is closed.
+ //
+
+ Status = LsapDbReferenceObject(
+ ConnectHandle,
+ 0,
+ PolicyObject,
+ ReferenceOptions
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenSecretError;
+ }
+
+ AcquiredLock = TRUE;
+ ContainerReferenced =TRUE;
+
+ //
+ // Setup Object Information prior to calling the LSA Database Object
+ // Open routine. The Object Type, Container Object Type and
+ // Logical Name (derived from the Sid) need to be filled in.
+ //
+
+ ObjectInformation.ObjectTypeId = SecretObject;
+ ObjectInformation.ContainerTypeId = PolicyObject;
+ ObjectInformation.Sid = NULL;
+
+ //
+ // Initialize the Object Attributes. The Container Object Handle and
+ // Logical Name (Internal Name) of the object must be set up.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectInformation.ObjectAttributes,
+ (PUNICODE_STRING)SecretName,
+ 0,
+ ConnectHandle,
+ NULL
+ );
+
+ //
+ // Open the specific Secret object. Note that the account object
+ // handle returned is an RPC Context Handle.
+ //
+
+ Status = LsapDbOpenObject(
+ &ObjectInformation,
+ DesiredAccess,
+ OpenOptions,
+ SecretHandle
+ );
+
+ //
+ // If the open failed, dereference the container object.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto OpenSecretError;
+ }
+
+OpenSecretFinish:
+
+ //
+ // If necessary, release the LSA Database lock.
+ //
+
+ if (AcquiredLock) {
+
+ LsapDbReleaseLock();
+ }
+
+#ifdef TRACK_HANDLE_CLOSE
+ if (*SecretHandle == LsapDbHandle)
+ {
+ DbgPrint("BUGBUG: Closing global policy handle\n");
+ DbgBreakPoint();
+ }
+#endif
+
+ return(Status);
+
+OpenSecretError:
+
+ //
+ // If necessary, dereference the Container Object handle. Note that
+ // this is only done in the error case. In the non-error case, the
+ // Container handle stays referenced until the Account object is
+ // closed.
+ //
+
+ if (ContainerReferenced) {
+
+ *SecretHandle = NULL;
+
+ Status = LsapDbDereferenceObject(
+ &ConnectHandle,
+ PolicyObject,
+ 0,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ goto OpenSecretFinish;
+}
+
+
+NTSTATUS
+LsarSetSecret(
+ IN LSAPR_HANDLE SecretHandle,
+ IN OPTIONAL PLSAPR_CR_CIPHER_VALUE CipherCurrentValue,
+ IN OPTIONAL PLSAPR_CR_CIPHER_VALUE CipherOldValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the LsaSetSecret
+ API.
+
+ The LsaSetSecret API optionally sets one or both values associated with
+ a Secret object. These values are known as the Current Value and
+ Old Value of the Secret object and these values have a meaning known to
+ the creator of the object.
+
+ This worker routine receives the Secret values in encrypted form from
+ the client. A two-way encryption algorithm using the Session Key will
+ havge been applied. The values received will first be decrypted using
+ this same key and then two-way encrypted using the LSA Database Private
+ Encryption Key. The resulting re-encrypted values will then be stored
+ as attributes of the Secret object.
+
+Arguments:
+
+ SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
+
+ CipherCurrentValue - Optional pointer to an encrypted value structure
+ containing the Current Value (if any) to be set for the Secret
+ Object (if any). This value is two-way encrypted with the Session
+ Key. If NULL is specified, the existing Current Value will be left
+ assigned to the object will be left unchanged.
+
+ CipherOldValue - Optional pointer to an encrypted value structure
+ containing the "old value" (if any) to be set for the Secret
+ Object (if any). If NULL is specified, the existing Old Value will be
+ assigned to the object will be left unchanged.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - Handle is invalid.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) SecretHandle;
+
+ PLSAP_CR_CLEAR_VALUE ClearCurrentValue = NULL;
+ PLSAP_CR_CLEAR_VALUE ClearOldValue = NULL;
+ PLSAP_CR_CIPHER_VALUE DbCipherCurrentValue = NULL;
+ ULONG DbCipherCurrentValueLength;
+ PLSAP_CR_CIPHER_VALUE DbCipherOldValue = NULL;
+ ULONG DbCipherOldValueLength;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+ LARGE_INTEGER UpdatedTime;
+ BOOLEAN ObjectReferenced = FALSE;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) SecretHandle;
+ ULONG ReferenceOptions = LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION;
+ ULONG DereferenceOptions = LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION;
+ BOOLEAN GlobalSecret;
+
+ //
+ // Check for Local Secret set request. If the Secret name does
+ // not begin with the Global Secret Prefix, the Secret is local. In
+ // this case, update of the secret is allowed on BDC's as well as
+ // PDC's and Workstations. Creation of Global Secrets is not
+ // allowed on BDC's except for trusted callers such as a Replicator.
+ //
+
+ Status = LsapDbGetScopeSecret(
+ (PLSAPR_UNICODE_STRING) &InternalHandle->LogicalNameU,
+ &GlobalSecret
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ if (!GlobalSecret) {
+
+ ReferenceOptions |= LSAP_DB_OMIT_BACKUP_CONTROLLER_CHECK;
+ DereferenceOptions |= LSAP_DB_OMIT_REPLICATOR_NOTIFICATION;
+ }
+
+ //
+ // If the client is non-trusted, obtain the Session Key used by the
+ // client to two-way encrypt the Current Value and/or Old Values.
+ //
+
+ if (!InternalHandle->Trusted) {
+
+ Status = LsapCrServerGetSessionKey( SecretHandle, &SessionKey);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the Secret Object handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle and open a database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ SecretHandle,
+ SECRET_SET_VALUE,
+ SecretObject,
+ ReferenceOptions
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // If a Current Value is specified for the Secret Object, and the
+ // client is non-trusted, decrypt the value using the Session Key and
+ // encrypt it using the LSA Database System Key. Then (for all
+ // clients) encrypt the resulting value with the internal LSA Database
+ // encryption key and write resulting Value structure (header followed by
+ // buffer to the Policy Database as the Current Value attribute of the
+ // Secret object. If no Current Value is specified, or a NULL
+ // string is specified, the existing Current Value will be deleted.
+ //
+
+ if (ARGUMENT_PRESENT(CipherCurrentValue)) {
+
+ if (!InternalHandle->Trusted) {
+
+ Status = LsapCrDecryptValue(
+ (PLSAP_CR_CIPHER_VALUE) CipherCurrentValue,
+ SessionKey,
+ &ClearCurrentValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ } else {
+
+ ClearCurrentValue = (PLSAP_CR_CLEAR_VALUE) CipherCurrentValue;
+ }
+
+ Status = LsapCrEncryptValue(
+ ClearCurrentValue,
+ LsapDbCipherKey,
+ &DbCipherCurrentValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ DbCipherCurrentValueLength = DbCipherCurrentValue->Length
+ + (ULONG) sizeof(LSAP_CR_CIPHER_VALUE);
+
+ } else {
+
+ DbCipherCurrentValue = NULL;
+ DbCipherCurrentValueLength = 0;
+ }
+
+ Status = LsapDbWriteAttributeObject(
+ SecretHandle,
+ &LsapDbNames[CurrVal],
+ DbCipherCurrentValue,
+ DbCipherCurrentValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ //
+ // Store the time at which the Current Secret value was last updated.
+ //
+
+ Status = NtQuerySystemTime(&UpdatedTime);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ Status = LsapDbWriteAttributeObject(
+ SecretHandle,
+ &LsapDbNames[CupdTime],
+ &UpdatedTime,
+ sizeof (LARGE_INTEGER)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ //
+ // If an Old Value is specified for the Secret Object, and the
+ // client is non-trusted, decrypt the value using the Session Key and
+ // encrypt it using the LSA Database System Key. Then (for all
+ // clients) encrypt the resulting value with the internal LSA Database
+ // encryption key and write resulting Value structure (header followed by
+ // buffer to the Policy Database as the Old Value attribute of the
+ // Secret object. If no Old Value is specified, or a NULL
+ // string is specified, the existing Old Value will be deleted.
+ //
+
+ if (ARGUMENT_PRESENT(CipherOldValue)) {
+
+ if (!InternalHandle->Trusted) {
+
+ Status = LsapCrDecryptValue(
+ (PLSAP_CR_CIPHER_VALUE) CipherOldValue,
+ SessionKey,
+ &ClearOldValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ } else {
+
+ ClearOldValue = (PLSAP_CR_CLEAR_VALUE) CipherOldValue;
+ }
+
+ Status = LsapCrEncryptValue(
+ ClearOldValue,
+ LsapDbCipherKey,
+ &DbCipherOldValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ DbCipherOldValueLength =
+ DbCipherOldValue->Length + (ULONG) sizeof(LSAP_CR_CIPHER_VALUE);
+
+ } else {
+
+ DbCipherOldValue = NULL;
+ DbCipherOldValueLength = 0;
+ }
+
+ Status = LsapDbWriteAttributeObject(
+ SecretHandle,
+ &LsapDbNames[OldVal],
+ DbCipherOldValue,
+ DbCipherOldValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ //
+ // Store the time at which the Old Secret value was last updated.
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ SecretHandle,
+ &LsapDbNames[OupdTime],
+ &UpdatedTime,
+ sizeof (LARGE_INTEGER)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+SetSecretFinish:
+
+ //
+ // If necessary, free memory allocated for the Session Key.
+ //
+
+ if (SessionKey != NULL) {
+
+ MIDL_user_free(SessionKey);
+ SessionKey = NULL;
+ }
+
+ //
+ // If necessary, free memory allocated for Decrypted Current Value.
+ // Note that for trusted clients, the decryption is the identity
+ // mapping, so do not do the free in this case.
+ //
+
+ if ((ClearCurrentValue != NULL) && !InternalHandle->Trusted) {
+
+ LsapCrFreeMemoryValue( ClearCurrentValue );
+ ClearCurrentValue = NULL;
+ }
+
+ //
+ // If necessary, free memory allocated for Decrypted Old Value.
+ // Note that for trusted clients, the decryption is the identity
+ // mapping, so do not do the free in this case.
+ //
+
+ if ((ClearOldValue != NULL) && !InternalHandle->Trusted) {
+
+ LsapCrFreeMemoryValue( ClearOldValue );
+ ClearOldValue = NULL;
+ }
+
+ //
+ // If necessary, free memory allocated for the Current Value
+ // encrypted for storage in the LSA Database.
+ //
+
+ if (DbCipherCurrentValue != NULL) {
+
+ LsapCrFreeMemoryValue( DbCipherCurrentValue );
+ DbCipherCurrentValue = NULL;
+ }
+
+ //
+ // If necessary, free memory allocated for the Old Value
+ // encrypted for storage in the LSA Database.
+ //
+
+ if (DbCipherOldValue != NULL) {
+
+ LsapCrFreeMemoryValue( DbCipherOldValue );
+ DbCipherOldValue = NULL;
+ }
+
+ //
+ // If necessary, dereference the Secret object, close the database
+ // transaction, notify the LSA Database Replicator of the change,
+ // release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &SecretHandle,
+ SecretObject,
+ DereferenceOptions,
+ SecurityDbChange,
+ Status
+ );
+ }
+
+ return(Status);
+
+SetSecretError:
+
+ goto SetSecretFinish;
+}
+
+
+NTSTATUS
+LsarQuerySecret(
+ IN LSAPR_HANDLE SecretHandle,
+ IN OUT OPTIONAL PLSAPR_CR_CIPHER_VALUE *CipherCurrentValue,
+ OUT OPTIONAL PLARGE_INTEGER CurrentValueSetTime,
+ IN OUT OPTIONAL PLSAPR_CR_CIPHER_VALUE *CipherOldValue,
+ OUT OPTIONAL PLARGE_INTEGER OldValueSetTime
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the LsaQuerySecret
+ API.
+
+ The LsaQuerySecret API optionally returns one or both of the values
+ assigned to a Secret object. These values are known as the "current value"
+ and the "old value", and they have a meaning known to the creator of the
+ Secret object. The values are returned in their encrypted form.
+ The caller must have LSA_QUERY_SECRET access to the Secret object.
+
+Arguments:
+
+ SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
+
+ CipherCurrentValue - Optional pointer to location which will receive a
+ pointer to an encrypted Unicode String structure containing the
+ "current value" of the Secret Object (if any) in encrypted form.
+ If no "current value" is assigned to the Secret object, a NULL pointer
+ is returned.
+
+ CurrentValueSetTime - The date/time at which the current secret value
+ was established.
+
+ CipherOldValue - Optional pointer to location which will receive a
+ pointer to an encrypted Unicode String structure containing the
+ "old value" of the Secret Object (if any) in encrypted form.
+ If no "old value" is assigned to the Secret object, a NULL pointer
+ is returned.
+
+ OldValueSetTime - The date/time at which the old secret value
+ was established.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) SecretHandle;
+ PLSAP_CR_CIPHER_VALUE OutputCipherCurrentValue = NULL;
+ PLSAP_CR_CIPHER_VALUE OutputCipherOldValue = NULL;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+ BOOLEAN ObjectReferenced = FALSE;
+ ULONG ValueSetTimeLength;
+
+ //
+ // If the caller is non-trusted, obtain the Session Key used by the
+ // client to two-way encrypt the Current Value and/or Old Values.
+ // Trusted Clients do not use encryption since they are calling
+ // this server service directly and not via RPC.
+ //
+
+ if (!InternalHandle->Trusted) {
+
+ Status = LsapCrServerGetSessionKey( SecretHandle, &SessionKey );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the Secret Object handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle and open a database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ SecretHandle,
+ SECRET_QUERY_VALUE,
+ SecretObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // If requested, query the Current Value attribute of the Secret Object.
+ // For non-trusted callers, the Current value will be returned in
+ // encrypted form embedded within a structure.
+ //
+
+ if (ARGUMENT_PRESENT(CipherCurrentValue)) {
+
+ Status = LsapDbQueryValueSecret(
+ SecretHandle,
+ &LsapDbNames[CurrVal],
+ SessionKey,
+ &OutputCipherCurrentValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+ }
+
+ //
+ // If requested, query the Old Value attribute of the Secret Object.
+ // For non-trusted callers, the Old Value will be returned in
+ // encrypted form embedded within a structure.
+ //
+
+ if (ARGUMENT_PRESENT(CipherOldValue)) {
+
+ Status = LsapDbQueryValueSecret(
+ SecretHandle,
+ &LsapDbNames[OldVal],
+ SessionKey,
+ &OutputCipherOldValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+ }
+
+ ValueSetTimeLength = sizeof (LARGE_INTEGER);
+
+ //
+ // If requested, Query the time at which the Current Value of the Secret
+ // was last established. If the Current Value has never been set, return
+ // the time at which the Secret was created.
+ //
+
+ if (ARGUMENT_PRESENT(CurrentValueSetTime)) {
+
+ Status = LsapDbReadAttributeObject(
+ SecretHandle,
+ &LsapDbNames[CupdTime],
+ CurrentValueSetTime,
+ &ValueSetTimeLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+ }
+
+ //
+ // If requested, Query the time at which the Old Value of the Secret
+ // was last established. If the Old Value has never been set, return
+ // the time at which the Secret was created.
+ //
+
+ if (ARGUMENT_PRESENT(OldValueSetTime)) {
+
+ Status = LsapDbReadAttributeObject(
+ SecretHandle,
+ &LsapDbNames[OupdTime],
+ OldValueSetTime,
+ &ValueSetTimeLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+ }
+
+QuerySecretFinish:
+
+ //
+ // If necessary, free memory allocated for the Session Key.
+ //
+
+ if (SessionKey != NULL) {
+
+ MIDL_user_free(SessionKey);
+ }
+
+ //
+ // Return Current and/or Old Values of Secret Object, or NULL to
+ // caller. In error cases, NULL will be returned.
+ //
+
+ if (ARGUMENT_PRESENT(CipherCurrentValue)) {
+
+ (PLSAP_CR_CIPHER_VALUE) *CipherCurrentValue = OutputCipherCurrentValue;
+ }
+
+ if (ARGUMENT_PRESENT(CipherOldValue)) {
+
+ (PLSAP_CR_CIPHER_VALUE) *CipherOldValue = OutputCipherOldValue;
+ }
+
+ //
+ // If necessary, dereference the Secret object, close the database
+ // transaction, release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &SecretHandle,
+ SecretObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ return(Status);
+
+QuerySecretError:
+
+ goto QuerySecretFinish;
+}
+
+
+NTSTATUS
+LsapDbQueryValueSecret(
+ IN LSAPR_HANDLE SecretHandle,
+ IN PUNICODE_STRING ValueName,
+ IN OPTIONAL PLSAP_CR_CIPHER_KEY SessionKey,
+ OUT PLSAP_CR_CIPHER_VALUE *CipherValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function queries the specified value of a Secret Object. If
+ the caller is non-trusted, the value returned will have been two-way
+ encrypted with the Session Key. If the caller is trusted, no
+ encryption is done since the caller is calling us directly.
+
+Arguments:
+
+ SecretHandle - Handle to Secret Object.
+
+ ValueName - Unicode name of the Secret Value to be queried. This
+ name is either "Currval" (for the Current Value) or "OldVal"
+ (for the Old Value.
+
+ SessionKey - Pointer to Session Key to be used for two-way encryption
+ of the value to be returned. This pointer must be non-NULL
+ except for Trusted Clients, where it must be NULL.
+
+ CipherValue - Receives 32-bit counted string pointer to Secret Value
+ queried. For non-trusted clients, the value will be encrypted.
+
+ WARNING - Note that CipherValue is defined to RPC as
+ "allocate(all_nodes)". This means that it is returned
+ in one contiguous block of memory rather than two, as
+ it would appear by the structure definition.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG DbCipherValueLength;
+ PLSAP_CR_CLEAR_VALUE ClearValue = NULL;
+ PLSAP_CR_CIPHER_VALUE DbCipherValue = NULL;
+ PLSAP_CR_CIPHER_VALUE OutputCipherValue = NULL;
+ LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) SecretHandle;
+
+ //
+ // Get length of the specified Value attribute of the Secret object.
+ //
+
+ DbCipherValueLength = 0;
+
+ Status = LsapDbReadAttributeObject(
+ SecretHandle,
+ ValueName,
+ NULL,
+ &DbCipherValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ goto QueryValueSecretError;
+ }
+
+ Status = STATUS_SUCCESS;
+ *CipherValue = NULL;
+ return(Status);
+ }
+
+ //
+ // We successfully read the length of the stored Secret Object value
+ // plus header from the Policy Database. Verify that the Secret
+ // Object value is either at least as long as a Cipher Value
+ // structure header, or is of length 0.
+ //
+
+ if (DbCipherValueLength < sizeof (LSAP_CR_CIPHER_VALUE)) {
+
+ if (DbCipherValueLength == 0) {
+
+ goto QueryValueSecretFinish;
+ }
+
+ Status = STATUS_INTERNAL_DB_ERROR;
+ goto QueryValueSecretError;
+ }
+
+ //
+ // Allocate memory for reading the specified Value of the Secret object.
+ // This value is stored in the Policy Database in the form of a
+ // Self-Relative Value structure. The Value Buffer part is encrypted.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ DbCipherValue = MIDL_user_allocate(DbCipherValueLength);
+
+ if (DbCipherValue == NULL) {
+
+ goto QueryValueSecretError;
+ }
+
+ //
+ // Read the specified Policy-database-encrypted Value attribute.
+ //
+
+ Status = LsapDbReadAttributeObject(
+ SecretHandle,
+ ValueName,
+ DbCipherValue,
+ &DbCipherValueLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryValueSecretError;
+ }
+
+ //
+ // Verify that Lengths in returned header are consistent
+ // and also match returned data length - header size.
+ //
+
+ Status = STATUS_INTERNAL_DB_ERROR;
+
+ if (DbCipherValue->Length > DbCipherValue->MaximumLength) {
+
+ goto QueryValueSecretError;
+ }
+
+ if ((DbCipherValue->Length + (ULONG) sizeof(LSAP_CR_CIPHER_VALUE)) !=
+ DbCipherValueLength) {
+
+ goto QueryValueSecretError;
+ }
+
+ //
+ // If the size of the Value structure is less than its header size,
+ // something is wrong.
+ //
+
+ if (DbCipherValueLength < sizeof(LSAP_CR_CIPHER_VALUE)) {
+
+ goto QueryValueSecretError;
+ }
+
+ //
+ // If the string length is 0, something is wrong.
+ //
+
+ if (DbCipherValue->Length == 0) {
+
+ goto QueryValueSecretError;
+ }
+
+ //
+ // Store pointer to Value buffer in the Value structure. This pointer
+ // points just after the header. Then decrypt the Value using the
+ // LSA Database Cipher Key and encrypt the result using the Session Key.
+ //
+
+ DbCipherValue->Buffer = (PUCHAR)(DbCipherValue + 1);
+
+ Status = LsapCrDecryptValue(
+ DbCipherValue,
+ LsapDbCipherKey,
+ &ClearValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryValueSecretError;
+ }
+
+ //
+ // If the client is non-Trusted, encrypt the value with the Session
+ // Key, otherwise, leave it unchanged.
+ //
+
+ if (!InternalHandle->Trusted) {
+
+ Status = LsapCrEncryptValue(
+ ClearValue,
+ SessionKey,
+ &OutputCipherValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QueryValueSecretError;
+ }
+
+ } else {
+
+ //
+ // Trusted clients get a clear-text block back.
+ // The block contains both the header and the text.
+ //
+
+ OutputCipherValue = (PLSAP_CR_CIPHER_VALUE)(ClearValue);
+ }
+
+QueryValueSecretFinish:
+
+ //
+ // If necessary, free memory allocated for the Db-encrypted Secret
+ // object Value read from the Policy Database.
+ //
+
+ if (DbCipherValue != NULL) {
+
+ LsapCrFreeMemoryValue( DbCipherValue );
+ }
+
+ //
+ // If necessary, free memory allocated for the Decrypted Value.
+ // Trusted client's get a pointer to ClearValue back, so don't
+ // free it in this case.
+ //
+
+ if (!InternalHandle->Trusted && ClearValue != NULL) {
+
+ LsapCrFreeMemoryValue( ClearValue );
+ }
+
+ //
+ // Return pointer to Cipher Value (Clear Value for trusted clients) or
+ // NULL.
+ //
+
+ *CipherValue = OutputCipherValue;
+ return(Status);
+
+QueryValueSecretError:
+
+ //
+ // If necessary, free memory allocated for the Secret object value
+ // after re-encryption for return to the Client.
+ //
+
+ if (OutputCipherValue != NULL) {
+
+ LsapCrFreeMemoryValue( OutputCipherValue );
+ }
+
+ goto QueryValueSecretFinish;
+}
+
+
+NTSTATUS
+LsaIEnumerateSecrets(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PVOID *Buffer,
+ IN ULONG PreferedMaximumLength,
+ OUT PULONG CountReturned
+ )
+
+/*++
+
+Routine Description:
+
+ This service returns information about Secret objects. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information.
+ To support this feature, the caller is provided with a handle that
+ can be used across calls to the API. On the initial call,
+ EnumerationContext should point to a variable that has been
+ initialized to 0.
+
+Arguments:
+
+ PolicyHandle - Trusted handle to an open Policy Object.
+
+ EnumerationContext - Zero-based index at which to start the enumeration.
+
+ Buffer - Receives a pointer to a buffer containing information for
+ one or more Secret objects. This information is an array of
+ structures of type UNICODE_STRING, with each entry providing the
+ name of a single Secret object. When this information is no
+ longer needed, it must be released using MIDL_user_free.
+
+ PreferedMaximumLength - Prefered maximum length of the returned
+ data (in 8-bit bytes). This is not a hard upper limit but
+ serves as a guide. Due to data conversion between systems
+ with different natural data sizes, the actual amount of data
+ returned may be greater than this value.
+
+ CountReturned - Numer of entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_NO_MORE_ENTRIES - No entries have been returned because
+ there are no more.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_NAME_ENUMERATION_BUFFER DbEnumerationBuffer;
+ PUNICODE_STRING SecretNames = NULL;
+ PUNICODE_STRING NextSecretName = NULL;
+ PSID *Sids = NULL;
+ LSAP_DB_ATTRIBUTE DomainNameAttribute;
+ LSAPR_HANDLE SecretHandle = NULL;
+ ULONG MaxLength;;
+
+ DomainNameAttribute.AttributeValue = NULL;
+
+ //
+ // If no Enumeration Structure is provided, return an error.
+ //
+
+
+ if ( !ARGUMENT_PRESENT(Buffer) ||
+ !ARGUMENT_PRESENT(EnumerationContext) ) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+
+ //
+ // Initialize the internal Lsa Database Enumeration Buffer, and
+ // the provided Enumeration Buffer to NULL.
+ //
+
+ DbEnumerationBuffer.EntriesRead = 0;
+ DbEnumerationBuffer.Names = NULL;
+ *Buffer = NULL;
+ DomainNameAttribute.AttributeValue = NULL;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Limit the enumeration length except for trusted callers
+ //
+
+ if ( !((LSAP_DB_HANDLE) PolicyHandle)->Trusted &&
+ (PreferedMaximumLength > LSA_MAXIMUM_ENUMERATION_LENGTH)
+ ) {
+ MaxLength = LSA_MAXIMUM_ENUMERATION_LENGTH;
+ } else {
+ MaxLength = PreferedMaximumLength;
+ }
+
+ //
+ // Call general enumeration routine. This will return an array
+ // of names of secrets.
+ //
+
+ Status = LsapDbEnumerateNames(
+ PolicyHandle,
+ SecretObject,
+ EnumerationContext,
+ &DbEnumerationBuffer,
+ MaxLength
+ );
+
+ //
+ // At this point:
+ //
+ // SUCCESS -> Some names are being returned (may or
+ // may not be additional names to be retrieved
+ // in future calls).
+ //
+ // NO_MORE_ENTRIES -> There are NO names to return
+ // for this or any future call.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Return the number of entries read. Note that the Enumeration Buffer
+ // returned from LsapDbEnumerateNames is expected to be non-null
+ // in all non-error cases.
+ //
+
+ ASSERT(DbEnumerationBuffer.EntriesRead != 0);
+
+
+ //
+ // Now copy the output array of Unicode Strings for the caller.
+ // Memory for the array and the Unicode Buffers is allocated via
+ // MIDL_user_allocate.
+ //
+
+ Status = LsapRpcCopyUnicodeStrings(
+ NULL,
+ DbEnumerationBuffer.EntriesRead,
+ &SecretNames,
+ DbEnumerationBuffer.Names
+ );
+ }
+
+ //
+ // Dereference retains current status value unless
+ // error occurs.
+ //
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+ }
+
+ //
+ // Fill in returned Enumeration Structure, returning 0 or NULL for
+ // fields in the error case.
+ //
+
+ *Buffer = SecretNames;
+ *CountReturned = DbEnumerationBuffer.EntriesRead;
+
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsaISetTimesSecret(
+ IN LSAPR_HANDLE SecretHandle,
+ IN PLARGE_INTEGER CurrentValueSetTime,
+ IN PLARGE_INTEGER OldValueSetTime
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used to set the times associated with a Secret object.
+ This allows the times of secrets to be set to what they are on the
+ Primary Domain Controller (PDC) involved in an LSA Database replication
+ rather than being set to the time at which the Secret object is
+ created on a Backup Domain Controller (BDC) being replicated to.
+
+Arguments:
+
+ SecretHandle - Trusted Handle to an open secret object. This will
+ have been obtained via a call to LsaCreateSecret() or LsaOpenSecret()
+ on which a Trusted Policy Handle was specified.
+
+ CurrentValueSetTime - The date and time to set for the date and time
+ at which the Current Value of the Secret object was set.
+
+ OldValueSetTime - The date and time to set for the date and time
+ at which the Old Value of the Secret object was set.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_ACCESS_DENIED - The supplied SecretHandle is not Trusted.
+
+ STATUS_INVALID_HANDLE - The supplied SecretHandle is not
+ a valid habdle to a Secret Object.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
+ LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) SecretHandle;
+ BOOLEAN ObjectReferenced = FALSE;
+
+ //
+ // Verify that both Times are specified.
+ //
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ if (!ARGUMENT_PRESENT( CurrentValueSetTime )) {
+
+ goto SetTimesSecretError;
+ }
+
+ if (!ARGUMENT_PRESENT( CurrentValueSetTime )) {
+
+ goto SetTimesSecretError;
+ }
+
+ //
+ // Acquire the Lsa Database lock. Verify that the Secret Object handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle and open a database transaction.
+ //
+
+ Status = LsapDbReferenceObject(
+ SecretHandle,
+ SECRET_SET_VALUE,
+ SecretObject,
+ LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION | LSAP_DB_TRUSTED
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetTimesSecretError;
+ }
+
+ ObjectReferenced = TRUE;
+
+ //
+ // Set the time at which the Current Secret value was last updated
+ // to the specified value.
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ SecretHandle,
+ &LsapDbNames[CupdTime],
+ CurrentValueSetTime,
+ sizeof (LARGE_INTEGER)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetTimesSecretError;
+ }
+
+ //
+ // Set the time at which the Old Secret value was last updated
+ // to the specified value.
+ //
+
+ Status = LsapDbWriteAttributeObject(
+ SecretHandle,
+ &LsapDbNames[OupdTime],
+ OldValueSetTime,
+ sizeof (LARGE_INTEGER)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetTimesSecretError;
+ }
+
+SetTimesSecretFinish:
+
+ //
+ // If necessary, dereference the Secret object, close the database
+ // transaction, notify the LSA Database Replicator of the change and
+ // release the LSA Database lock and return.
+ //
+
+ if (ObjectReferenced) {
+
+ Status = LsapDbDereferenceObject(
+ &SecretHandle,
+ SecretObject,
+ LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
+ SecurityDbChange,
+ Status
+ );
+ }
+
+ return(Status);
+
+SetTimesSecretError:
+
+ goto SetTimesSecretFinish;
+}
+
+
+NTSTATUS
+LsapDbGetScopeSecret(
+ IN PLSAPR_UNICODE_STRING SecretName,
+ OUT PBOOLEAN GlobalSecret
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks the scope of a Secret name. Secrets have either
+ Global or Local Scope.
+
+ Global Secrets are Secrets that are normally present on all DC's for a
+ Domain. They are replicated from PDC's to BDC's. On BDC's, only a
+ Trusted Client such as a replicator can create, update or delete Global
+ Secrets. Global Secrets are identified as Secrets whose name begins
+ with a designated prefix.
+
+ Local Secrets are Secrets that are private to a specific machine. They
+ are not replicated. Normal non-trusted clients may create, update or
+ delete Local Secrets. Local Secrets are idientified as Secrets whose
+ name does not begin with a designated prefix.
+
+Arguments:
+
+ SecretName - Pointer to Unicode String containing the name of the
+ Secret to be checked.
+
+ GlobalSecret - Receives a Boolean indicating
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The Secret name is valid
+
+ STATUS_INVALID_PARAMETER - The Secret Name is invalid in such a
+ way as to prevent scope determination.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ UNICODE_STRING GlobalPrefix;
+ BOOLEAN OutputGlobalSecret = FALSE;
+
+ //
+ // Initialize a Unicode String with the Global Secret name Prefix.
+ //
+
+ RtlInitUnicodeString( &GlobalPrefix, LSA_GLOBAL_SECRET_PREFIX );
+
+ //
+ // Now check if the given Name has the Global Prefix.
+ //
+
+ if (RtlPrefixUnicodeString( &GlobalPrefix, (PUNICODE_STRING) SecretName, TRUE)) {
+
+ OutputGlobalSecret = TRUE;
+ }
+
+ *GlobalSecret = OutputGlobalSecret;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapDbBuildSecretCache(
+ )
+
+/*++
+
+Routine Description:
+
+ This function builds a cache of Secret Objects. Currently, it is not
+ implemented
+
+Arguments:
+
+ None
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ return(Status);
+}
+
diff --git a/private/lsa/server/dirs b/private/lsa/server/dirs
new file mode 100644
index 000000000..5bc41674b
--- /dev/null
+++ b/private/lsa/server/dirs
@@ -0,0 +1 @@
+DIRS=dll exe
diff --git a/private/lsa/server/dll/makefile b/private/lsa/server/dll/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/server/dll/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/server/dll/sources b/private/lsa/server/dll/sources
new file mode 100644
index 000000000..1048c7a40
--- /dev/null
+++ b/private/lsa/server/dll/sources
@@ -0,0 +1,109 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=server
+
+TARGETNAME=lsasrv
+TARGETPATH=obj
+TARGETPATHLIB=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+DLLDEF=..\lsasrv.def
+DLLORDER=..\lsasrv.prf
+
+INCLUDES=..\;..\..\inc;..\..\..\inc
+
+
+NTPROFILEINPUT=yes
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= ..\adtinit.c \
+ ..\adtbuild.c \
+ ..\adtevent.c \
+ ..\adtlog.c \
+ ..\adtobjs.c \
+ ..\aucred.c \
+ ..\auclient.c \
+ ..\aufilter.c \
+ ..\auinit.c \
+ ..\auctxt.c \
+ ..\aulogon.c \
+ ..\auloop.c \
+ ..\aumsp.c \
+ ..\aupckg.c \
+ ..\auproc.c \
+ ..\ausess.c \
+ ..\crserver.c \
+ ..\dbadmin.c \
+ ..\dbaccnt.c \
+ ..\dbattr.c \
+ ..\dbdata.c \
+ ..\dbdomain.c \
+ ..\dbhandle.c \
+ ..\dbinit.c \
+ ..\dbinstac.c \
+ ..\dbinstal.c \
+ ..\dblookup.c \
+ ..\dbmisc.c \
+ ..\dbobject.c \
+ ..\dbpob.c \
+ ..\dbpolicy.c \
+ ..\dbpriv.c \
+ ..\dbsamtst.c \
+ ..\dbsecret.c \
+ ..\lsa_rev.rc \
+ ..\lsaerror.c \
+ ..\lsaifree.c \
+ ..\lsainit.c \
+ ..\lsarm.c \
+ ..\lsarpc_s.c \
+ ..\lsasrvmm.c \
+ ..\lsawrap.c \
+ ..\oldstub.c \
+ ..\rpcinit.c \
+ ..\sepriv.c \
+ ..\services.c \
+ ..\lsa_rev.rc \
+ ..\lsapmsgs.mc
+
+PASS0_HEADERDIR=.
+PASS0_SOURCEDIR=.
+
+UMTYPE=windows
+
+TARGETLIBS= \
+ ..\..\common\obj\*\lsacomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\nlrepl.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+USE_CRTDLL=1
diff --git a/private/lsa/server/exe/makefile b/private/lsa/server/exe/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/server/exe/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/server/exe/sources b/private/lsa/server/exe/sources
new file mode 100644
index 000000000..2605c4f2b
--- /dev/null
+++ b/private/lsa/server/exe/sources
@@ -0,0 +1,57 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=server
+
+TARGETNAME=lsass
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+INCLUDES=..\;..\..\inc;..\..\..\inc
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ ..\lsass.c \
+ ..\main.c \
+ ..\lsa_rev.rc \
+ ..\lsapmsgs.mc
+
+PASS0_HEADERDIR=.
+PASS0_SOURCEDIR=.
+
+UMTYPE=windows
+UMENTRYABS=mainNoCRTStartup
+
+BACKGROUND_USE=1
+
+TARGETLIBS=\
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\lsasrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
diff --git a/private/lsa/server/lsa_rev.rc b/private/lsa/server/lsa_rev.rc
new file mode 100644
index 000000000..6aa4f4a47
--- /dev/null
+++ b/private/lsa/server/lsa_rev.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "LSA Executable and Server DLL"
+#define VER_INTERNALNAME_STR "lsasrv.dll and lsass.exe"
+
+#include "common.ver"
+RCINCLUDE lsapmsgs.rc
+
diff --git a/private/lsa/server/lsaerror.c b/private/lsa/server/lsaerror.c
new file mode 100644
index 000000000..12da382e6
--- /dev/null
+++ b/private/lsa/server/lsaerror.c
@@ -0,0 +1,64 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsaerror.c
+
+Abstract:
+
+ Local Security Authority Protected Subsystem - Error Routines
+
+Author:
+
+ Scott Birrell (ScottBi) April 30, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+
+
+VOID
+LsapLogError(
+ IN OPTIONAL PUCHAR Message,
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This function retrieves the status of Lsa Initialization.
+
+Arguments:
+
+ Message - Optional Message to be printed out if debugging enabled.
+
+ Status - Standard Nt Result Code supplied by calling routine.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#if DBG
+
+ if (ARGUMENT_PRESENT(Message)) {
+
+ DbgPrint( Message, Status );
+ }
+
+#endif //DBG
+
+}
+
+
diff --git a/private/lsa/server/lsaifree.c b/private/lsa/server/lsaifree.c
new file mode 100644
index 000000000..d6a639d62
--- /dev/null
+++ b/private/lsa/server/lsaifree.c
@@ -0,0 +1,552 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ lsaifree.c
+
+Abstract:
+
+ This file contains routines to free structure allocated by the lsar
+ routines. These routines are used by lsa clients which live in the
+ lsae process as the lsa server and call the lsar routines directly.
+
+
+Author:
+
+ Scott Birrell (ScottBi) April 15, 1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+
+--*/
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Includes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+#include <lsasrvp.h>
+#include <lsaisrv.h>
+
+
+VOID
+LsaiFree_LSAPR_SR_SECURITY_DESCRIPTOR (
+ PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and the graph of allocated subnodes
+ pointed to by an LSAPR_SR_SECURITY_DESCRIPTOR structure.
+
+Parameters:
+
+ Source - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( SecurityDescriptor )) {
+
+ _fgs__LSAPR_SR_SECURITY_DESCRIPTOR ( SecurityDescriptor );
+ MIDL_user_free ( SecurityDescriptor );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_ACCOUNT_ENUM_BUFFER (
+ PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the graph of allocated subnodes pointed to by
+ an LSAPR_ACCOUNT_ENUM_BUFFER structure. The structure itself is
+ left intact.
+
+Parameters:
+
+ EnumerationBuffer - A pointer to the node whose graph of subnodes
+ is to be freed.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT(EnumerationBuffer)) {
+
+ _fgs__LSAPR_ACCOUNT_ENUM_BUFFER ( EnumerationBuffer );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_TRANSLATED_SIDS (
+ PLSAPR_TRANSLATED_SIDS TranslatedSids
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated subnodes pointed to by
+ an LSAPR_TRANSLATED_SIDS structure.
+
+Parameters:
+
+ TranslatedSids - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( TranslatedSids )) {
+
+ _fgs__LSAPR_TRANSLATED_SIDS ( TranslatedSids );
+ MIDL_user_free( TranslatedSids );
+ }
+}
+
+
+
+VOID
+LsaIFree_LSAPR_TRANSLATED_NAMES (
+ PLSAPR_TRANSLATED_NAMES TranslatedNames
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated subnodes pointed to by
+ an LSAPR_TRANSLATED_NAMES structure.
+
+Parameters:
+
+ TranslatedNames - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( TranslatedNames )) {
+
+ _fgs__LSAPR_TRANSLATED_NAMES( TranslatedNames );
+ MIDL_user_free( TranslatedNames );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_POLICY_INFORMATION (
+ POLICY_INFORMATION_CLASS InformationClass,
+ PLSAPR_POLICY_INFORMATION PolicyInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated nodes pointed to by
+ an LSAPR_POLICY_INFORMATION structure.
+
+Parameters:
+
+ PolicyInformation - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( PolicyInformation )) {
+
+ _fgu__LSAPR_POLICY_INFORMATION ( PolicyInformation, InformationClass );
+ MIDL_user_free( PolicyInformation );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO (
+ TRUSTED_INFORMATION_CLASS InformationClass,
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated subnodes pointed to by
+ an LSAPR_TRUSTED_DOMAIN_INFO structure.
+
+Parameters:
+
+ InformationClass - Specifies the Trusted Domain Information Class
+ to which the TrustedDomainInformation relates.
+
+ TrustedDomainInformation - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( TrustedDomainInformation )) {
+
+ _fgu__LSAPR_TRUSTED_DOMAIN_INFO ( TrustedDomainInformation, InformationClass );
+ MIDL_user_free( TrustedDomainInformation );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_REFERENCED_DOMAIN_LIST (
+ PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated nodes pointed to by
+ an LSAPR_REFERENCED_DOMAIN_LIST structure.
+
+Parameters:
+
+ ReferencedDomains - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( ReferencedDomains )) {
+
+ _fgs__LSAPR_REFERENCED_DOMAIN_LIST ( ReferencedDomains );
+ MIDL_user_free( ReferencedDomains );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER (
+ PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the graph of allocated nodes pointed to by
+ an LSAPR_TRUST_INFORMATION structure. The structure itself is
+ left intact.
+
+Parameters:
+
+ EnumerationBuffer - A pointer to the node whose graph of subnodes
+ is to be freed.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( EnumerationBuffer )) {
+
+ _fgs__LSAPR_TRUSTED_ENUM_BUFFER ( EnumerationBuffer );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_TRUST_INFORMATION (
+ PLSAPR_TRUST_INFORMATION TrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated nodes pointed to by
+ an LSAPR_TRUST_INFORMATION structure.
+
+Parameters:
+
+ TrustInformation - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( TrustInformation )) {
+
+ _fgs__LSAPR_TRUST_INFORMATION ( TrustInformation );
+ MIDL_user_free( TrustInformation );
+ }
+}
+
+
+VOID
+LsaIFree_LSAI_SECRET_ENUM_BUFFER (
+ PVOID EnumerationBuffer,
+ ULONG Count
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the graph of allocated subnodes pointed to by
+ an LSAI_SECRET_ENUM_BUFFER structure. The structure itself is
+ left intact.
+
+Parameters:
+
+ EnumerationBuffer - A pointer to the node whose graph of subnodes
+ is to be freed.
+
+ Count - Count of the number of Entries in the structure.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ULONG Index;
+
+ PLSAPR_UNICODE_STRING EnumerationBufferU = (PLSAPR_UNICODE_STRING) EnumerationBuffer;
+
+ if ( ARGUMENT_PRESENT( EnumerationBuffer)) {
+
+ for (Index = 0; Index < Count; Index++ ) {
+
+ _fgs__LSAPR_UNICODE_STRING( &EnumerationBufferU[Index] );
+ }
+
+ MIDL_user_free( EnumerationBufferU );
+ }
+}
+
+
+VOID
+LsaIFree_LSAI_PRIVATE_DATA (
+ PVOID Data
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees a structure containing LSA Private Database
+ Information.
+
+Parameters:
+
+ Data - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( Data )) {
+
+ MIDL_user_free( Data );
+ }
+
+}
+
+
+VOID
+LsaIFree_LSAPR_SR_SECURITY_DESCRIPTOR (
+ PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated subnodes pointed to by
+ an LSAPR_SR_SECURITY_DESCRIPTOR structure.
+
+Parameters:
+
+ SecurityDescriptor - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( SecurityDescriptor )) {
+
+ _fgs__LSAPR_SR_SECURITY_DESCRIPTOR( SecurityDescriptor );
+ MIDL_user_free( SecurityDescriptor );
+ }
+}
+
+
+
+
+VOID
+LsaIFree_LSAPR_UNICODE_STRING (
+ IN PLSAPR_UNICODE_STRING UnicodeName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated subnodes pointed to by
+ an LSAPR_UNICODE_STRING structure.
+
+Parameters:
+
+ UnicodeName - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( UnicodeName )) {
+
+ _fgs__LSAPR_UNICODE_STRING( UnicodeName );
+ MIDL_user_free( UnicodeName );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_PRIVILEGE_SET (
+ IN PLSAPR_PRIVILEGE_SET PrivilegeSet
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated subnodes pointed to by
+ an LSAPR_PRIVILEGE_SET structure.
+
+Parameters:
+
+ PrivilegeSet - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT( PrivilegeSet )) {
+
+ MIDL_user_free( PrivilegeSet );
+ }
+}
+
+
+VOID
+LsaIFree_LSAPR_CR_CIPHER_VALUE (
+ IN PLSAPR_CR_CIPHER_VALUE CipherValue
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the node and graph of allocated subnodes pointed to by
+ an LSAPR_CR_CIPHER_VALUE structure. Note that this structure is in
+ fact allocated(all_nodes) on the server side of LSA.
+
+Parameters:
+
+ CipherValue - A pointer to the node to free.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ MIDL_user_free( CipherValue );
+}
+
+
+VOID
+LsaIFree_LSAPR_PRIVILEGE_ENUM_BUFFER (
+ PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the graph of allocated subnodes pointed to by
+ an LSAPR_PRIVILEGE_ENUM_BUFFER structure. The structure itself is
+ left intact.
+
+Parameters:
+
+ EnumerationBuffer - A pointer to the node whose graph of subnodes
+ is to be freed.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if (ARGUMENT_PRESENT(EnumerationBuffer)) {
+
+ _fgs__LSAPR_PRIVILEGE_ENUM_BUFFER ( EnumerationBuffer );
+ }
+}
diff --git a/private/lsa/server/lsainit.c b/private/lsa/server/lsainit.c
new file mode 100644
index 000000000..9f722daef
--- /dev/null
+++ b/private/lsa/server/lsainit.c
@@ -0,0 +1,843 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsainit.c
+
+Abstract:
+
+ Local Security Authority Protected Subsystem - Initialization
+
+Author:
+
+ Scott Birrell (ScottBi) March 12, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "adtp.h"
+
+//
+// Name of event which says that the LSA RPC server is ready
+//
+
+#define LSA_RPC_SERVER_ACTIVE L"LSA_RPC_SERVER_ACTIVE"
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Shared Global Variables //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+#if LSAP_DIAGNOSTICS
+//
+// LSA Global Controls
+//
+
+ULONG LsapGlobalFlag = 0;
+#endif //LSAP_DIAGNOSTICS
+
+
+
+
+//
+// Handles used to talk to SAM directly.
+// Also, a flag to indicate whether or not the handles are valid.
+//
+
+
+BOOLEAN LsapSamOpened = FALSE;
+
+SAMPR_HANDLE LsapAccountDomainHandle;
+SAMPR_HANDLE LsapBuiltinDomainHandle;
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Module-Wide variables //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+ LsapHealthCheckingEnabled = FALSE;
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Internal routine prototypes //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+NTSTATUS
+LsapActivateRpcServer();
+
+DWORD
+LsapRpcServerThread(
+ LPVOID Parameter
+ );
+
+NTSTATUS
+LsapInstallationPause();
+
+VOID
+LsapSignalRpcIsActive();
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapInitLsa(
+ )
+
+/*++
+
+Routine Description:
+
+ This process is activated as a standard SM subsystem. Initialization
+ completion of a SM subsystem is indicated by having the first thread
+ exit with status.
+
+ This function initializes the LSA. The initialization procedure comprises
+ the following steps:
+
+ o LSA Heap Initialization
+ o LSA Command Server Initialization
+ o LSA Database Load
+ o Reference Monitor State Initialization
+ o LSA RPC Server Initialization
+ o LSA Auditing Initialization
+ o LSA Authentication Services Initialization
+ o Wait for Setup to complete (if necessary)
+ o LSA database initialization (product type-specific)
+
+ Any failure in any of the above steps is fatal and causes the LSA
+ process to terminate. The system must be aborted.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN BooleanStatus = TRUE;
+ BOOLEAN AuditingInitPass1Success = TRUE;
+
+
+ //
+ // Initialize the LSA's heap.
+ //
+
+ Status = LsapHeapInitialize();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+
+ //
+ // Initialize a copy of the Well-Known Sids, etc. for use by
+ // the LSA.
+ //
+
+ Status = LsapDbInitializeWellKnownValues();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+ //
+ // Perform LSA Command Server Initialization. This involves creating
+ // an LPC port called the LSA Command Server Port so that the Reference
+ // monitor can send commands to the LSA via the port. After the port
+ // is created, an event created by the Reference Monitor is signalled,
+ // so that the Reference Monitor can proceed to connect to the port.
+
+ Status = LsapRmInitializeServer();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+ //
+ // Disable Replicator Notifications.
+ //
+
+ LsapDbDisableReplicatorNotification();
+
+ //
+ // Perform LSA Database Server Initialization - Pass 1.
+ // This initializes the non-product-type-specific information.
+ //
+
+ Status = LsapDbInitializeServer(1);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+ //
+ // Perform RPC Server Initialization.
+ //
+
+ Status = LsapRPCInit();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+ //
+ // Perform Auditing Initialization - Pass 1.
+ //
+
+ LsapAdtInitializationPass = 1;
+
+ Status = LsapAdtInitialize(LsapAdtInitializationPass);
+
+ if (!NT_SUCCESS(Status)) {
+
+ AuditingInitPass1Success = FALSE;
+
+ Status = STATUS_SUCCESS;
+ }
+
+
+ Status = LsapAdtObjsInitialize();
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ //
+ // Initialize Authentication Services
+ //
+
+ if (!LsapAuInit()) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto InitLsaError;
+ }
+
+ /*
+ Status = LsapAuInit();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+ */
+
+
+ //
+ // Start processing RPC calls
+ //
+
+ Status = LsapActivateRpcServer();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+ //
+ // Pause for installation if necessary
+ //
+
+ Status = LsapInstallationPause();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+ //
+ // Perform LSA Database Server Initialization - Pass 2.
+ // This initializes the product-type-specific information.
+ //
+
+ LsapAdtInitializationPass = 2;
+
+ Status = LsapDbInitializeServer(LsapAdtInitializationPass);
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitLsaError;
+ }
+
+ //
+ // Enable Replicator Notifications.
+ //
+
+ LsapDbEnableReplicatorNotification();
+
+ //
+ // Perform Auditing Initialization - Pass 2.
+ // This pass writes out any remaining cached Audit Records collected during
+ // initialization.
+ //
+
+ if (AuditingInitPass1Success) {
+
+ Status = LsapAdtInitialize(2);
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // Enable health checking within lsa
+ //
+
+ LsaIHealthCheck( LsaIHealthLsaInitialized );
+
+InitLsaFinish:
+
+ return(Status);
+
+InitLsaError:
+
+ goto InitLsaFinish;
+}
+
+
+NTSTATUS
+LsapActivateRpcServer( VOID )
+
+
+/*++
+
+Routine Description:
+
+ This function creates a thread for the RPC server.
+ The new Thread then goes on to activate the RPC server,
+ which causes RPC calls to be delivered when recieved.
+
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+
+ STATUS_SUCCESS - The thread was successfully created.
+
+ Other status values that may be set by CreateThread().
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ ULONG WaitCount = 0;
+
+ // Start listening for remote procedure calls. The first
+ // argument to RpcServerListen is the minimum number of call
+ // threads to create; the second argument is the maximum number
+ // of concurrent calls allowed. The final argument indicates that
+ // this routine should not wait. After everything has been initialized,
+ // we return.
+
+ Status = I_RpcMapWin32Status(RpcServerListen(1, 1234, 1));
+
+ ASSERT( Status == RPC_S_OK );
+
+ //
+ // Set event which signals that RPC server is available.
+ //
+
+ LsapSignalRpcIsActive();
+
+ return(STATUS_SUCCESS);
+
+
+}
+
+NTSTATUS
+LsapInstallationPause( VOID )
+
+
+/*++
+
+Routine Description:
+
+ This function checks to see if the system is in an
+ installation state. If so, it suspends further initialization
+ until the installation state is complete.
+
+ Installation state is signified by the existance of a well known
+ event.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+
+ STATUS_SUCCESS - Proceed with initialization.
+
+ Other status values are unexpected.
+
+--*/
+
+{
+
+
+ NTSTATUS NtStatus, TmpStatus;
+ HANDLE InstallationEvent;
+ OBJECT_ATTRIBUTES EventAttributes;
+ UNICODE_STRING EventName;
+
+
+ //
+ // If the following event exists, it is an indication that
+ // installation is in progress and that further security
+ // initialization should be delayed until the event is
+ // signalled. This is expected to be a NOTIFICATION event.
+ //
+
+ RtlInitUnicodeString( &EventName, L"\\INSTALLATION_SECURITY_HOLD");
+ InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
+
+ NtStatus = NtOpenEvent(
+ &InstallationEvent,
+ SYNCHRONIZE,
+ &EventAttributes
+ );
+
+ if ( NT_SUCCESS(NtStatus)) {
+
+ //
+ // The event exists - installation created it and will signal it
+ // when it is ok to proceed with security initialization.
+ //
+
+ LsapSetupWasRun = TRUE;
+
+ //
+ // Installation code is responsible for deleting the event after
+ // signalling it.
+ //
+
+ NtStatus = NtWaitForSingleObject( InstallationEvent, TRUE, 0 );
+ TmpStatus = NtClose( InstallationEvent );
+ ASSERT(NT_SUCCESS(TmpStatus));
+ } else {
+ NtStatus = STATUS_SUCCESS; // Indicate everything is as expected
+ }
+
+ return(NtStatus);
+
+}
+
+
+BOOLEAN
+LsaISetupWasRun(
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether Setup was run.
+
+Arguments:
+
+ None
+
+Return Values
+
+ BOOLEAN - TRUE if setup was run, else FALSE
+
+--*/
+
+{
+ return(LsapSetupWasRun);
+}
+
+
+VOID
+LsapSignalRpcIsActive(
+ )
+/*++
+
+Routine Description:
+
+ It creates the LSA_RPC_SERVER_ACTIVE event if one does not already exist
+ and signals it so that the service controller can proceed with LSA calls.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+ HANDLE EventHandle;
+
+
+ EventHandle = CreateEventW(
+ NULL, // No special security
+ TRUE, // Must be manually reset
+ FALSE, // The event is initially not signalled
+ LSA_RPC_SERVER_ACTIVE
+ );
+
+ if (EventHandle == NULL) {
+
+ status = GetLastError();
+
+ //
+ // If the event already exists, the service controller beats us
+ // to creating it. Just open it.
+ //
+
+ if (status == ERROR_ALREADY_EXISTS) {
+
+ EventHandle = OpenEventW(
+ GENERIC_WRITE,
+ FALSE,
+ LSA_RPC_SERVER_ACTIVE
+ );
+ }
+
+ if (EventHandle == NULL) {
+ //
+ // Could not create or open the event. Nothing we can do...
+ //
+ return;
+ }
+ }
+
+ (VOID) SetEvent(EventHandle);
+}
+
+
+NTSTATUS
+LsapGetAccountDomainInfo(
+ PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine retrieves ACCOUNT domain information from the LSA
+ policy database.
+
+
+Arguments:
+
+ PolicyAccountDomainInfo - Receives a pointer to a
+ POLICY_ACCOUNT_DOMAIN_INFO structure containing the account
+ domain info.
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ Other status values that may be returned from:
+
+ LsaOpenPolicy()
+ LsaQueryInformationPolicy()
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+
+ LSA_HANDLE PolicyHandle;
+ OBJECT_ATTRIBUTES PolicyObjectAttributes;
+
+ //
+ // Open the policy database
+ //
+
+ InitializeObjectAttributes( &PolicyObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ Status = LsaOpenPolicy( NULL,
+ &PolicyObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle );
+ if ( NT_SUCCESS(Status) ) {
+
+
+ //
+ // Query the account domain information
+ //
+
+ Status = LsaQueryInformationPolicy( PolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *) PolicyAccountDomainInfo );
+#if DBG
+ if ( NT_SUCCESS(Status) ) {
+ ASSERT( (*PolicyAccountDomainInfo) != NULL );
+ ASSERT( (*PolicyAccountDomainInfo)->DomainSid != NULL );
+ }
+#endif // DBG
+
+
+ IgnoreStatus = LsaClose( PolicyHandle );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapOpenSam( VOID )
+
+/*++
+
+Routine Description:
+
+ This routine opens SAM for use during authentication. It
+ opens a handle to both the BUILTIN domain and the ACCOUNT domain.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ SAMPR_HANDLE SamHandle;
+ HANDLE EventHandle;
+ OBJECT_ATTRIBUTES EventAttributes;
+ UNICODE_STRING EventName;
+ LARGE_INTEGER Timeout;
+
+
+ if (LsapSamOpened == TRUE) { // Global variable
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // Make sure SAM has initialized
+ //
+
+ RtlInitUnicodeString( &EventName, L"\\SAM_SERVICE_STARTED");
+ InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
+ Status = NtOpenEvent( &EventHandle, SYNCHRONIZE, &EventAttributes );
+ ASSERT( Status == STATUS_SUCCESS || Status == STATUS_OBJECT_NAME_NOT_FOUND );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // See if SAM has signalled that he is initialized.
+ //
+
+ Timeout.QuadPart = -10000000; // 1000 seconds
+ Timeout.QuadPart *= 1000;
+ Status = NtWaitForSingleObject( EventHandle, FALSE, &Timeout );
+ IgnoreStatus = NtClose( EventHandle );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ }
+
+ if ( !NT_SUCCESS(Status) || Status == STATUS_TIMEOUT ) {
+
+ return( STATUS_INVALID_SERVER_STATE );
+ }
+
+
+ //
+ // Get the member Sid information for the account domain
+ //
+
+ Status = LsapGetAccountDomainInfo( &PolicyAccountDomainInfo );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+
+ //
+ // Get our handles to the ACCOUNT and BUILTIN domains.
+ //
+
+ Status = SamIConnect( NULL, // No server name
+ &SamHandle,
+ SAM_SERVER_CONNECT,
+ TRUE ); // Indicate we are privileged
+
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // Open the ACCOUNT domain.
+ //
+
+ Status = SamrOpenDomain( SamHandle,
+ DOMAIN_ALL_ACCESS,
+ PolicyAccountDomainInfo->DomainSid,
+ &LsapAccountDomainHandle );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Open the BUILTIN domain.
+ //
+
+
+ Status = SamrOpenDomain( SamHandle,
+ DOMAIN_ALL_ACCESS,
+ LsapBuiltInDomainSid,
+ &LsapBuiltinDomainHandle );
+
+
+ if (NT_SUCCESS(Status)) {
+
+ LsapSamOpened = TRUE;
+
+ } else {
+
+ IgnoreStatus = SamrCloseHandle( &LsapAccountDomainHandle );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ }
+ }
+
+ IgnoreStatus = SamrCloseHandle( &SamHandle );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ }
+
+ //
+ // Free the ACCOUNT domain information
+ //
+
+ LsaFreeMemory( PolicyAccountDomainInfo );
+
+ return(Status);
+}
+
+
+
+
+VOID
+LsaIHealthCheck(
+ IN ULONG CallerId
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to perform sanity checks within LSA.
+ It is here to aid in diagnosing and isolating problems within
+ LSA.
+
+Arguments:
+
+ CallerId - Identifies the caller (look in \nt\private\inc\lsaisrv.h).
+
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+
+ LSAP_DB_HANDLE InternalHandle;
+
+ ////////////////////////////////////////////////////////////////////
+ // //
+ // Conditions being addressed: //
+ // //
+ // //
+ // Bug 24194 is one in which someone is writting the string //
+ // "Domains\Account\Users\000001F4" on top of the //
+ // LsapPolicyHandle data structure. In a retail system, we //
+ // can't really do anything. In a debug system we will //
+ // breakpoint when we encounter this situation. //
+ // //
+ ////////////////////////////////////////////////////////////////////
+
+
+ //
+ // LSA Initialization
+ //
+
+ if (CallerId == LsaIHealthLsaInitialized) {
+ LsapHealthCheckingEnabled = TRUE;
+ return;
+ }
+
+
+
+
+
+
+ if (!LsapHealthCheckingEnabled) {
+ return;
+ }
+
+ //
+ // Bug 24194
+ //
+
+#if DBG
+ if ( (CallerId == LsaIHealthSamJustLocked) ||
+ (CallerId == LsaIHealthSamAboutToFree) ){
+ InternalHandle = (LSAP_DB_HANDLE)LsapPolicyHandle;
+ if (InternalHandle->Next->Previous != InternalHandle) {
+ DbgPrint( "LSA Internal Failure: (*LsapPolicyHandle) overwritten.\n"
+ " Breaking for debug.\n");
+ DbgBreakPoint();
+ }
+ }
+#endif DBG
+
+
+ return;
+
+}
diff --git a/private/lsa/server/lsapmsgs.mc b/private/lsa/server/lsapmsgs.mc
new file mode 100644
index 000000000..392de365c
--- /dev/null
+++ b/private/lsa/server/lsapmsgs.mc
@@ -0,0 +1,147 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1991-1993 Microsoft Corporation
+;
+;Module Name:
+;
+; lsapmsgs.mc
+;
+;Abstract:
+;
+; LSA localizable text
+;
+;Author:
+;
+; Jim Kelly 1-Apr-1993
+;
+;Revision History:
+;
+;Notes:
+;
+;
+;--*/
+;
+;#ifndef _LSAPMSGS_
+;#define _LSAPMSGS_
+;
+;/*lint -save -e767 */ // Don't complain about different definitions // winnt
+
+MessageIdTypedef=DWORD
+
+
+;//
+;// Force facility code message to be placed in .h file
+;//
+MessageId=0x1FFF SymbolicName=LSAP_UNUSED_MESSAGE
+Language=English
+.
+
+
+;////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Well Known SID & RID Names //
+;//
+;//
+;// //
+;// //
+;////////////////////////////////////////////////////////////////////////////
+
+
+MessageId=0x2000 SymbolicName=LSAP_SID_NAME_NULL
+Language=English
+NULL SID
+.
+
+MessageId=0x2001 SymbolicName=LSAP_SID_NAME_WORLD
+Language=English
+Everyone
+.
+
+MessageId=0x2002 SymbolicName=LSAP_SID_NAME_LOCAL
+Language=English
+LOCAL
+.
+
+MessageId=0x2003 SymbolicName=LSAP_SID_NAME_CREATOR_OWNER
+Language=English
+CREATOR OWNER
+.
+
+MessageId=0x2004 SymbolicName=LSAP_SID_NAME_CREATOR_GROUP
+Language=English
+CREATOR GROUP
+.
+
+MessageId=0x2005 SymbolicName=LSAP_SID_NAME_NT_DOMAIN
+Language=English
+NT Pseudo Domain
+.
+
+MessageId=0x2006 SymbolicName=LSAP_SID_NAME_NT_AUTHORITY
+Language=English
+NT AUTHORITY
+.
+
+MessageId=0x2007 SymbolicName=LSAP_SID_NAME_DIALUP
+Language=English
+DIALUP
+.
+
+MessageId=0x2008 SymbolicName=LSAP_SID_NAME_NETWORK
+Language=English
+NETWORK
+.
+
+MessageId=0x2009 SymbolicName=LSAP_SID_NAME_BATCH
+Language=English
+BATCH
+.
+
+MessageId=0x200A SymbolicName=LSAP_SID_NAME_INTERACTIVE
+Language=English
+INTERACTIVE
+.
+
+MessageId=0x200B SymbolicName=LSAP_SID_NAME_SERVICE
+Language=English
+SERVICE
+.
+
+MessageId=0x200C SymbolicName=LSAP_SID_NAME_BUILTIN
+Language=English
+BUILTIN
+.
+
+MessageId=0x200D SymbolicName=LSAP_SID_NAME_SYSTEM
+Language=English
+SYSTEM
+.
+
+MessageId=0x200E SymbolicName=LSAP_SID_NAME_ANONYMOUS
+Language=English
+ANONYMOUS LOGON
+.
+
+MessageId=0x200f SymbolicName=LSAP_SID_NAME_CREATOR_OWNER_SERVER
+Language=English
+CREATOR OWNER SERVER
+.
+
+MessageId=0x2010 SymbolicName=LSAP_SID_NAME_CREATOR_GROUP_SERVER
+Language=English
+CREATOR GROUP SERVER
+.
+
+MessageId=0x2011 SymbolicName=LSAP_SID_NAME_SERVER
+Language=English
+SERVER LOGON
+.
+
+
+
+
+;/*lint -restore */ // Resume checking for different macro definitions // winnt
+;
+;
+;#endif // _LSAPMSGS_
diff --git a/private/lsa/server/lsarm.c b/private/lsa/server/lsarm.c
new file mode 100644
index 000000000..df499480d
--- /dev/null
+++ b/private/lsa/server/lsarm.c
@@ -0,0 +1,660 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsarm.c
+
+Abstract:
+
+ Local Security Authority - Reference Monitor Communication
+
+Author:
+
+ Scott Birrell (ScottBi) March 26, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+
+//
+// LSA Global State
+//
+
+LSAP_STATE LsapState;
+
+//
+// Lsa Reference Monitor Server Command Dispatch Table
+//
+
+PLSA_COMMAND_WORKER LsapCommandDispatch[] = {
+
+ LsapComponentTestWrkr,
+ LsapAdtWriteLogWrkr,
+ LsapComponentTestWrkr,
+ LsapLogonSessionDeletedWrkr
+
+};
+
+
+VOID
+LsapRmServerThread(
+ )
+
+/*++
+
+Routine Description:
+
+ This function is executed by the LSA Reference Monitor Server Thread. This
+ thread receives messages from the Reference Monitor. Examples of messages
+ include Audit Messages,... The function is implemented as a for loop
+ which runs indefinitely unless an error occurs. Currently, any
+ error is fatal. On each iteration a message is received from the
+ Reference Monitor and dispatched to a handler.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None. Any return is a fatal error.
+
+--*/
+
+{
+ PLSA_REPLY_MESSAGE Reply;
+ LSA_COMMAND_MESSAGE CommandMessage;
+ LSA_REPLY_MESSAGE ReplyMessage;
+
+ NTSTATUS Status;
+
+ //
+ // Initialize the LPC port message header type and data sizes for
+ // for the reply message.
+ //
+
+ ReplyMessage.MessageHeader.u2.ZeroInit = 0;
+ ReplyMessage.MessageHeader.u1.s1.TotalLength =
+ (CSHORT) sizeof(RM_COMMAND_MESSAGE);
+ ReplyMessage.MessageHeader.u1.s1.DataLength =
+ ReplyMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // First time through, there is no reply.
+ //
+
+ Reply = NULL;
+
+ //
+ // Now loop indefinitely, processing incoming Command Message Packets
+ //
+
+ for(;;) {
+
+ //
+ // Wait for and receive a message from the Reference Monitor through
+ // the Lsa Command LPC Port.
+ //
+
+ Status = NtReplyWaitReceivePort(
+ LsapState.LsaCommandPortHandle,
+ NULL,
+ (PPORT_MESSAGE) Reply,
+ (PPORT_MESSAGE) &CommandMessage
+ );
+
+
+ if (Status != 0) {
+ if (!NT_SUCCESS( Status ) &&
+ Status != STATUS_INVALID_CID &&
+ Status != STATUS_UNSUCCESSFUL
+ ) {
+ KdPrint(("LSASS: Lsa message receive from Rm failed x%lx\n", Status));
+ }
+
+ //
+ // Ignore if client went away.
+ //
+
+ Reply = NULL;
+ continue;
+ }
+
+ //
+ // If an LPC request, process it.
+ //
+
+ if (CommandMessage.MessageHeader.u2.s2.Type == LPC_REQUEST) {
+
+ //
+ //
+ // Now dispatch to a routine to handle the command. Allow
+ // command errors to occur without bringing system down.
+ //
+
+ Reply = &ReplyMessage;
+ ReplyMessage.MessageHeader.ClientId =
+ CommandMessage.MessageHeader.ClientId;
+ ReplyMessage.MessageHeader.MessageId =
+ CommandMessage.MessageHeader.MessageId;
+
+ Status = (LsapCommandDispatch[CommandMessage.CommandNumber])(
+ &CommandMessage,
+ Reply);
+
+ ReplyMessage.ReturnedStatus = Status;
+ } else {
+
+ Reply = NULL;
+ }
+
+ } // end_for
+
+ return;
+}
+
+
+BOOLEAN
+LsapRmInitializeServer(
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the Lsa Reference Monitor Server Thread.
+ The following steps are performed.
+
+ o Create the Lsa Command LPC Port
+ o Open the Lsa Init event created by the Reference Monitor
+ o Signal the Lsa Init Event, telling RM to go ahead and connect
+ to the port
+ o Connect to the Reference Monitor Command Port as client
+ o Listen for the Reference Monitor to connect to the port
+ o Accept the connection to the port
+ o Complete the connection to the port
+ o Create the LSA Reference Monitor Server Thread
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, else FALSE
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ BOOLEAN BooleanStatus = FALSE;
+
+ PORT_MESSAGE ConnectionRequest;
+ REMOTE_PORT_VIEW ClientView;
+
+ HANDLE LsaInitEventHandle;
+ OBJECT_ATTRIBUTES LsaInitEventObjA;
+ UNICODE_STRING LsaInitEventName;
+
+ UNICODE_STRING RmCommandPortName, LsaCommandPortName;
+
+ OBJECT_ATTRIBUTES LsaCommandPortObjA;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+
+ HANDLE Thread;
+ DWORD Ignore;
+
+ //
+ // Create the Lsa Command LPC Port. This port will receive
+ // commands from the Reference Monitor.
+ //
+
+ RtlInitUnicodeString( &LsaCommandPortName, L"\\SeLsaCommandPort" );
+
+ //
+ // Setup to create LSA Command Port
+ //
+
+ InitializeObjectAttributes(
+ &LsaCommandPortObjA,
+ &LsaCommandPortName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ Status = NtCreatePort(
+ &LsapState.LsaCommandPortHandle,
+ &LsaCommandPortObjA,
+ 0,
+ sizeof(LSA_COMMAND_MESSAGE),
+ sizeof(LSA_COMMAND_MESSAGE) * 32
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsapRmInitializeServer - Port Create failed 0x%lx\n",Status));
+ goto InitServerError;
+ }
+
+ //
+ // Open the LSA Init Event created by the Reference Monitor
+ //
+
+ RtlInitUnicodeString( &LsaInitEventName, L"\\SeLsaInitEvent" );
+
+ InitializeObjectAttributes(
+ &LsaInitEventObjA,
+ &LsaInitEventName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenEvent(
+ &LsaInitEventHandle,
+ EVENT_MODIFY_STATE,
+ &LsaInitEventObjA
+ );
+
+ //
+ // If the LSA Init event could not be opened, the LSA cannot
+ // synchronize with the Reference Monitor so neither component will
+ // function correctly.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsapRmInitializeServer - Lsa Init Event Open failed 0x%lx\n",Status));
+ goto InitServerError;
+ }
+
+ //
+ // Signal the LSA Init Event. If the signalling fails, the LSA
+ // is not able to synchronize properly with the Reference Monitor.
+ // This is a serious error which prevents both components from
+ // functioning correctly.
+ //
+
+ Status = NtSetEvent( LsaInitEventHandle, NULL );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsapRmInitializeServer - Init Event Open failed 0x%lx\n",Status));
+ goto InitServerError;
+ }
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port. Use the most efficient (least overhead) - which is dynamic
+ // rather than static tracking.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ //
+ // Connect to the Reference Monitor Command Port. This port
+ // is used to send commands from the LSA to the Reference Monitor.
+ //
+
+ RtlInitUnicodeString( &RmCommandPortName, L"\\SeRmCommandPort" );
+
+ Status = NtConnectPort(
+ &LsapState.RmCommandPortHandle,
+ &RmCommandPortName,
+ &DynamicQos,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsapRmInitializeServer - Connect to Rm Command Port failed 0x%lx\n",Status));
+ goto InitServerError;
+ }
+
+ //
+ // Listen for the Reference Monitor To Connect to the LSA
+ // Command Port.
+ //
+
+ ConnectionRequest.u1.s1.TotalLength = sizeof(ConnectionRequest);
+ ConnectionRequest.u1.s1.DataLength = (CSHORT)0;
+ Status = NtListenPort(
+ LsapState.LsaCommandPortHandle,
+ &ConnectionRequest
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsapRmInitializeServer - Port Listen failed 0x%lx\n",Status));
+ goto InitServerError;
+ }
+
+ //
+ // Accept the connection to the Lsa Command Port.
+ //
+
+ ClientView.Length = sizeof(ClientView);
+ Status = NtAcceptConnectPort(
+ &LsapState.LsaCommandPortHandle,
+ NULL,
+ &ConnectionRequest,
+ TRUE,
+ NULL,
+ &ClientView
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsapRmInitializeServer - Port Accept Connect failed 0x%lx\n",Status));
+
+ goto InitServerError;
+ }
+
+ //
+ // Complete the connection
+ //
+
+ Status = NtCompleteConnectPort(LsapState.LsaCommandPortHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("LsapRmInitializeServer - Port Complete Connect failed 0x%lx\n",Status));
+ goto InitServerError;
+ }
+
+ //
+ // Create the LSA Reference Monitor Server Thread
+ //
+
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) LsapRmServerThread,
+ (LPVOID)0,
+ 0L,
+ &Ignore
+ );
+
+ if (Thread == NULL) {
+
+ KdPrint(("LsapRmInitializeServer - Create Thread failed 0x%lx\n",Status));
+ }
+
+ BooleanStatus = TRUE;
+
+ goto InitServerCleanup;
+
+InitServerError:
+
+ //
+ // Perform cleanup needed only in error cases here.
+ //
+
+InitServerCleanup:
+
+ //
+ // Perform cleanup needed in all cases here
+ //
+
+ return BooleanStatus;
+}
+
+
+NTSTATUS
+LsapCallRm(
+ IN RM_COMMAND_NUMBER CommandNumber,
+ IN OPTIONAL PVOID CommandParams,
+ IN ULONG CommandParamsLength,
+ OUT OPTIONAL PVOID ReplyBuffer,
+ IN ULONG ReplyBufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function sends a command to the Reference Monitor from the LSA via
+ the LSA Command LPC Port. This function must only be called from within
+ Lsa code. If the command has parameters, they will be copied directly
+ into a message structure and sent via LPC, therefore, the supplied
+ parameters may not contain any absolute pointers. A caller must remove
+ pointers by "marshalling" them into the buffer CommandParams.
+
+ To implement a new RM command, do the following:
+ ================================================
+
+ (1) Provide in the executive an RM worker routine called
+ SepRm<command>Wrkr to service the command. See file
+ ntos\se\rmmain.c for examples. NOTE: If the command takes
+ parameters, they must not contain any absolute pointers (addresses).
+
+ (2) In file private\inc\ntrmlsa.h, append the name of the new command to
+ the enumerated type RM_COMMAND_NUMBER. Change the #define for
+ RmMaximumCommand to reference the new command.
+
+ (3) Add the SepRm<command>Wrkr to the command dispatch table structure
+ SepRmCommandDispatch[] in file ntos\se\rmmain.c.
+
+ (4) Add function prototypes to lsap.h and sep.h.
+
+Arguments:
+
+ CommandNumber - Specifies the command
+
+ CommandParams - Optional command-dependent parameters. The parameters
+ must be in marshalled format, that is, there must not be any
+ absolute address pointers in the buffer.
+
+ CommandParamsLength - Length in bytes of command parameters. Must be 0
+ if no command parameters supplied.
+
+ ReplyBuffer - Reply Buffer in which data (if any) from the command will
+ be returned.
+
+ ReplyBufferLength - Length of ReplyBuffer in bytes.
+
+Return Value:
+
+ NTSTATUS - Result Code. This is either a result code returned from
+ trying to send the command/receive the reply, or a status code
+ from the command itself.
+
+--*/
+
+{
+ NTSTATUS Status;
+ RM_COMMAND_MESSAGE CommandMessage;
+ RM_REPLY_MESSAGE ReplyMessage;
+
+ //
+ // Assert that the Command Number is valid.
+ //
+
+ ASSERT( CommandNumber >= RmMinimumCommand &&
+ CommandNumber <= RmMaximumCommand );
+
+ //
+ // If command parameters are supplied, assert that the length of the
+ // command parameters is positive and not too large. If no command
+ // parameters are supplied, assert that the length field is 0.
+ //
+
+ ASSERT( ( ARGUMENT_PRESENT( CommandParams ) &&
+ CommandParamsLength > 0 &&
+ CommandParamsLength <= RM_MAXIMUM_COMMAND_PARAM_SIZE ) ||
+
+ ( !ARGUMENT_PRESENT( CommandParams ) &&
+ CommandParamsLength == 0 )
+ );
+
+ //
+ // If a Reply Buffer is provided, assert that its length is > 0
+ // and not too large.
+ //
+
+ ASSERT( ( ARGUMENT_PRESENT( ReplyBuffer ) &&
+ ReplyBufferLength > 0 &&
+ ReplyBufferLength <= LSA_MAXIMUM_REPLY_BUFFER_SIZE ) ||
+
+ ( !ARGUMENT_PRESENT( ReplyBuffer ) &&
+ ReplyBufferLength == 0 )
+ );
+
+ //
+ // Construct a message for LPC. First, fill in the message header
+ // fields for LPC, specifying the message type and data sizes for
+ // the outgoing CommandMessage and the incoming ReplyMessage.
+ //
+
+ CommandMessage.MessageHeader.u2.ZeroInit = 0;
+ CommandMessage.MessageHeader.u1.s1.TotalLength =
+ ((CSHORT) RM_COMMAND_MESSAGE_HEADER_SIZE +
+ (CSHORT) CommandParamsLength);
+ CommandMessage.MessageHeader.u1.s1.DataLength =
+ CommandMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ ReplyMessage.MessageHeader.u2.ZeroInit = 0;
+ ReplyMessage.MessageHeader.u1.s1.DataLength = (CSHORT) ReplyBufferLength;
+ ReplyMessage.MessageHeader.u1.s1.TotalLength =
+ ReplyMessage.MessageHeader.u1.s1.DataLength +
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // Next, fill in the header info needed by the Reference Monitor.
+ //
+
+ CommandMessage.CommandNumber = CommandNumber;
+ ReplyMessage.ReturnedStatus = STATUS_SUCCESS;
+
+ //
+ // Finally, copy the command parameters (if any) into the message buffer.
+ //
+
+ if (CommandParamsLength > 0) {
+
+ RtlCopyMemory(CommandMessage.CommandParams,CommandParams,CommandParamsLength);
+ }
+
+ // Send Message to the RM via the RM Command Server LPC Port
+ //
+
+ Status = NtRequestWaitReplyPort(
+ LsapState.RmCommandPortHandle,
+ (PPORT_MESSAGE) &CommandMessage,
+ (PPORT_MESSAGE) &ReplyMessage
+ );
+
+ //
+ // If the command was successful, copy the data back to the output
+ // buffer.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Move output from command (if any) to buffer. Note that this
+ // is done even if the command returns status, because some status
+ // values are not errors.
+ //
+
+ if (ARGUMENT_PRESENT(ReplyBuffer)) {
+
+ RtlCopyMemory(
+ ReplyBuffer,
+ ReplyMessage.ReplyBuffer,
+ ReplyBufferLength
+ );
+
+ }
+
+ //
+ // Return status from command.
+ //
+
+ Status = ReplyMessage.ReturnedStatus;
+
+ } else {
+
+ KdPrint(("Security: Command sent from LSA to RM returned 0x%lx\n",Status));
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+LsapComponentTestWrkr(
+ IN PLSA_COMMAND_MESSAGE CommandMessage,
+ OUT PLSA_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function processes the Component Test LSA Rm Server command.
+ This is a temporary command that can be used to verifiey that the link
+ from RM to LSA is working.
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing LSA command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (LsapComponentTestCommand). This command
+ currently has one parameter, the fixed value 0x1234567.
+
+ ReplyMessage - Pointer to structure containing LSA reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ STATUS_SUCCESS - The test call has completed successfully.
+
+ STATUS_INVALID_PARAMETER - The argument value received was not the
+ expected argument value.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Strict check that command is correct.
+ //
+
+ ASSERT( CommandMessage->CommandNumber == LsapComponentTestCommand );
+
+ KdPrint(("Security: LSA Component Test Command Received\n"));
+
+ //
+ // Verify that the parameter value passed is as expected.
+ //
+
+ if (*((ULONG *) CommandMessage->CommandParams) !=
+ LSA_CT_COMMAND_PARAM_VALUE ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ UNREFERENCED_PARAMETER(ReplyMessage); // Intentionally not referenced
+ return(Status);
+}
diff --git a/private/lsa/server/lsasrv.def b/private/lsa/server/lsasrv.def
new file mode 100644
index 000000000..e777d1602
--- /dev/null
+++ b/private/lsa/server/lsasrv.def
@@ -0,0 +1,104 @@
+LIBRARY LSASRV
+
+DESCRIPTION 'Local Security Authority Server Trusted Client Library'
+
+EXPORTS
+
+;
+; Scott Birrell (ScottBi) April 9, 1992
+;
+; Exported Trusted Lsa Services
+;
+; These services are used only by Trusted Clients. These clients are
+; part of the Security Process
+;
+
+;
+; Standard LSA Server Side services. These services form the
+; server side of the LSA's RPC interface, so each service corresponds
+; to a published LSA Client API. A Trusted LSA Client, being part of the
+; Security Process; executable image, may call these services directly.
+;
+ LsarLookupNames
+ LsarLookupSids
+ LsarClose
+ LsarDelete
+ LsarLookupPrivilegeValue
+ LsarLookupPrivilegeName
+ LsarLookupPrivilegeDisplayName
+ LsarEnumeratePrivileges
+ LsarSetSecurityObject
+ LsarQuerySecurityObject
+
+ LsarOpenPolicy
+ LsarQueryInformationPolicy
+ LsarSetInformationPolicy
+
+ LsarCreateAccount
+ LsarOpenAccount
+ LsarEnumerateAccounts
+ LsarEnumeratePrivilegesAccount
+ LsarAddPrivilegesToAccount
+ LsarRemovePrivilegesFromAccount
+ LsarGetQuotasForAccount
+ LsarSetQuotasForAccount
+ LsarGetSystemAccessAccount
+ LsarSetSystemAccessAccount
+
+ LsarCreateTrustedDomain
+ LsarOpenTrustedDomain
+ LsarQueryInfoTrustedDomain
+ LsarSetInformationTrustedDomain
+ LsarEnumerateTrustedDomains
+
+ LsarCreateSecret
+ LsarOpenSecret
+ LsarSetSecret
+ LsarQuerySecret
+
+;
+; Trusted LSA Server Side interfaces. These interfaces are used only
+; by trusted LSA Clients, such as netlogon.dll. They do not form
+; part of the LSA's RPC interface.
+
+ LsaIHealthCheck
+ LsaIOpenPolicyTrusted
+ LsaIQueryInformationPolicyTrusted
+ LsaIGetSerialNumberPolicy
+ LsaIGetSerialNumberPolicy2
+ LsaISetSerialNumberPolicy
+ LsaIGetPrivateData
+ LsaISetPrivateData
+ LsaIEnumerateSecrets
+ LsaISetTimesSecret
+ LsaIAuditSamEvent
+ LsaIAuditNotifyPackageLoad
+
+ LsaIFree_LSAPR_ACCOUNT_ENUM_BUFFER
+ LsaIFree_LSAPR_TRANSLATED_SIDS
+ LsaIFree_LSAPR_TRANSLATED_NAMES
+ LsaIFree_LSAPR_POLICY_INFORMATION
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO
+ LsaIFree_LSAPR_REFERENCED_DOMAIN_LIST
+ LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER
+ LsaIFree_LSAPR_TRUST_INFORMATION
+ LsaIFree_LSAI_SECRET_ENUM_BUFFER
+ LsaIFree_LSAPR_SR_SECURITY_DESCRIPTOR
+ LsaIFree_LSAI_PRIVATE_DATA
+ LsaIFree_LSAPR_UNICODE_STRING
+ LsaIFree_LSAPR_PRIVILEGE_SET
+ LsaIFree_LSAPR_CR_CIPHER_VALUE
+ LsaIFree_LSAPR_PRIVILEGE_ENUM_BUFFER
+
+;
+; Interfaces used only within the LSA.
+;
+ LsapInitLsa
+ LsapAuOpenSam
+ ServiceInit
+ LsaISetupWasRun
+
+;
+; Interfaces used only by the Policy Component Test (ctlsarpc)
+;
+ LsaIInitializeWellKnownSids
diff --git a/private/lsa/server/lsasrv.prf b/private/lsa/server/lsasrv.prf
new file mode 100644
index 000000000..47d5513ae
--- /dev/null
+++ b/private/lsa/server/lsasrv.prf
@@ -0,0 +1,254 @@
+LsapFreeLsaHeap@4
+LsapAllocateLsaHeap@4
+LsapAuDereferenceClientContext@4
+LsapAuProvideWorkerThreads@0
+LsapCopyToClientBuffer@16
+LsapAllocateClientBuffer@12
+LsapAuApiDispatchCallPackage@8
+LsapAuInterlockedDecrement@4
+LsapAuInterlockedIncrement@4
+LsapAuReferenceClientContext@12
+LsapCopyFromClientBuffer@16
+LsapGetLogonSession@8
+NdrServerInitializeNew@12
+RpcImpersonateClient@4
+I_RpcGetBuffer@4
+NdrPointerUnmarshall@16
+NdrPointerFree@12
+RpcRevertToSelf@0
+MIDL_user_allocate@4
+NdrPointerMarshall@12
+MIDL_user_free@4
+NdrPointerBufferSize@12
+LsapRpcCopyUnicodeString@12
+LsapQueryClientInfo@8
+LsarGetUserName@12
+lsarpc_LsarGetUserName@4
+LsapGetCredentials@28
+LsapGetPackageCredentials@12
+LsapReturnCredential@20
+LsapDbReferenceObject@16
+LsapDbJoinSubPaths@12
+LsapRtlCopyUnicodeString@12
+LsapDbResetStates@16
+LsapDbReadAttributeObject@16
+LsapDbSetStates@4
+LsapDbVerifyInformationObject@4
+lsarpc_LsarOpenPolicy2@4
+LsapDbVerifyHandle@12
+LsapDbReleaseLock@0
+RtlpNtOpenKey@16
+LsapDbLookupHandle@4
+LsapDbOpenPolicy@20
+NdrServerContextMarshall@12
+LsapDbOpenObject@16
+NDRSContextUnmarshall@8
+LsapDbCreateHandle@8
+LsapDbDereferenceObject@20
+LsapDbRequestAccessObject@16
+I_RpcMapWin32Status@4
+NdrServerContextUnmarshall@4
+LsapDbGetNamesObject@12
+RtlpNtQueryValueKey@20
+LsapDbIsLocked@0
+LsapDbAcquireLock@0
+NdrSimpleStructUnmarshall@16
+LsarOpenPolicy2@16
+NdrSimpleTypeUnmarshall@12
+LsapDbQueryInformationPolicy@12
+LsarQueryInformationPolicy@12
+lsarpc_LsarQueryInformationPolicy@4
+LsarClose@4
+lsarpc_LsarClose@4
+LsapDbFreeHandle@4
+LsapDbCloseObject@8
+LsapDbVerifyInfoQueryPolicy@12
+LsapDbValidInfoPolicy@8
+LsapDbReleaseReadLockTrustedDomainList@4
+LsapDbEnumerateTrustedDomains@16
+LsapDbIsValidTrustedDomainList@4
+LsapDbTraverseTrustedDomainList@16
+NdrSimpleStructBufferSize@12
+NdrSimpleStructMarshall@12
+LsapDbAcquireReadLockTrustedDomainList@4
+LsapDbEnumerateTrustedDomainList@16
+LsarEnumerateTrustedDomains@16
+lsarpc_LsarEnumerateTrustedDomains@4
+LsapDbLocateEntryNumberTrustedDomainList@20
+LsaIQueryInformationPolicyTrusted@8
+LsapAssignInitialHiveProtection@4
+NlLoadNetlogonDll@0
+LsapDbSlowEnumerateTrustedDomains@16
+LsapDbInitializePrivilegeObject@0
+LsapAuAddClientContext@4
+LsapGetPrivilegeDllNames@8
+LsapDbBuildAccountCache@0
+SamIConnect@16
+LsapCaptureClientTokenGroups@16
+LsapDbQueryAllInformationAccounts@16
+LsapCreateLogonSession@4
+LsapAdtInitializeLogQueue@0
+LsapAuApiDispatchLogonUser@8
+LsapFreeTokenGroups@4
+LsapCallRm@20
+LsapAuVerifyLogonType@8
+LsaIFree_LSAPR_POLICY_INFORMATION@8
+LsapRtlGetPrivilege@8
+LsapAuSetLogonPrivilegeStates@12
+_fgs__LSAPR_POLICY_PRIMARY_DOM_INFO@4
+LsapFreeTokenInformationV1@4
+LsapSetDefaultDacl@20
+LsapAuCopySid@12
+SamrGetAliasMembership@12
+LsapAuCopySidAndAttributes@12
+LsapAuUserLogonPolicyFilter@20
+LsapAuAddStandardIds@20
+LsapRtlAddPrivileges@20
+SystemFunction004@12
+LsapAuOpenSam@0
+LsapDbLookupAccount@8
+LsapQueryPackageName@4
+_fgs__LSAPR_UNICODE_STRING@4
+LsaIHealthCheck@4
+LsapAdtAuditLogon@56
+LsapAuSetTokenInformation@32
+LsapCreateV1Token@20
+LsapAdtAuditSpecialPrivileges@16
+_fgu__LSAPR_POLICY_INFORMATION@8
+LsarSetInformationPolicy@12
+LsapAuSetPassedIds@20
+LsapDbSidToLogicalNameObject@8
+LsapDbWriteAttributeObject@16
+LsapCrEncryptValue@12
+LsapDbOpenRootRegistryKey@0
+LsapValidateProgrammaticNames@4
+LsapAuThreadManager@4
+LsapDbInitializeContainingDirs@0
+LsapDbLookupInitialize@0
+LsapRPCInit@0
+LsapAdtQueryAuditLogFullInfo@12
+LsapDbMakeUnicodeAttribute@12
+LsaIInitializeWellKnownSids@4
+LsapSetLogonSessionAccountInfo@20
+LsapRtlPrivilegeSetToLuidAndAttributes@12
+LsapValidLogonProcess@12
+LsarOpenAccount@16
+LsapDbWellKnownSidName@4
+_allmul
+LsapFreeTokenInformationNull@4
+LsapOpenSam@0
+LsapDbWriteAttributesObject@12
+LsapIncorporateLocalGroups@12
+LsapIsSidLogonSid@4
+LsapDbSlowQuerySystemAccessAccount@8
+LsapDbInitializeWellKnownValues@0
+LsapDbInitializeRights@0
+LsapDbMakeSidAttribute@12
+LsapAddCredential@16
+LsapInitLsa@0
+LsapDbInitializeServer@4
+LsapAdtAuditingLogon@4
+LsapDbOpenTransaction@0
+LsapAuCreateServerThreads@0
+LsapDbSlowQueryPrivilegesAccount@8
+LsapDbSlowQueryQuotasAccount@8
+LsapSignalRpcIsActive@0
+LsapDbCreateAccountList@4
+LsapAuLoopInitialize@0
+LsapDbSlowQueryAllInformationAccounts@16
+LsapMmAllocateMidl@12
+LsapAuAddLocalAliases@16
+LsapDbBuildSecretCache@0
+_fgs__LSAPR_POLICY_AUDIT_EVENTS_INFO@4
+LsapAddPackage@12
+LsapAdtInitialize@4
+RpcpAddInterface@8
+ServiceDispatcherThread@4
+LsaIInitializeWellKnownSid@32
+LsapAdtAuditPackageLoad@4
+LsapCrUnicodeToClearValue@8
+LsaClose@4
+LsapAuInitializeContextMgr@0
+LsapDbSlowQueryInformationAccount@12
+LsapInstallationPause@0
+LsapValidatePrivilegeDlls@0
+LsapDbInitializeHandleTable@0
+RpcpReadSDFromRegistry@8
+LsapAdtObjsInitialize@0
+LsaFreeMemory@4
+LsapDbReadAttributesObject@12
+LsapDbInitWellKnownPrivilegeName@8
+LsapDbMakeInvalidInformationPolicy@4
+LsapDbInitializeObjectTypes@0
+LsapPackageInitialize@0
+LsapDbNotifyRoleChangePolicy@4
+LsapDbUpgradeRevision@0
+LsapBuildWorldSynchSD@12
+LsapAuServerLoop@4
+LsapDbLogicalToPhysicalSubKey@12
+LsarEnumerateAccounts@16
+LsapDbRebuildCache@4
+LsapDbBuildPolicyCache@0
+LsapActivateRpcServer@0
+LsapAdtInitializeDriveLetters@0
+LsapDbInitializeWellKnownPrivs@0
+LsapDbCopyUnicodeAttribute@12
+LsapAuGetCountOfThreadsToCreate@8
+ServiceInit@0
+RtlpNtEnumerateSubKey@16
+LsaOpenPolicy@16
+LsapAdtReleaseLogQueueLock@0
+wcslen
+LsapDbCreateAccount@8
+I_NetNotifyRole@4
+LsapAdtAuditLogonProcessRegistration@4
+LsapAdtAcquireLogQueueLock@0
+LsapDbDisableReplicatorNotification@0
+LsapDbEnumerateSids@20
+LsapEnableCreateTokenPrivilege@0
+wcscpy
+LsapDbFindNextSid@16
+LsapDbSlowQueryInformationPolicy@12
+LsapAdtSystemRestart@4
+LsaIOpenPolicyTrusted@4
+LsapHeapInitialize@0
+wcscat
+LsapDbInitializeCipherKey@0
+LsapDbBuildObjectCaches@0
+LsapConfigurePackage@24
+LsapConfigurePackages@0
+_fgs__LSAPR_POLICY_ACCOUNT_DOM_INFO@4
+LsapRmServerThread@0
+LsapAuHandleConnectionRequest@4
+LdrLoadDll@16
+RpcServerRegisterIf@12
+LsapDbInitializeReplication@0
+LsapDbReleaseWriteLockTrustedDomainList@4
+LsapDbApplyTransaction@12
+LsapDbInitializeLock@0
+LsapDbBuildTrustedDomainList@8
+LsapOpenPrivilegeDlls@0
+LsapLogonSessionInitialize@0
+LsapAuCreatePortSD@4
+LsapAuInit@0
+LsapDbVerifyInfoSetPolicy@16
+LsapDbBuildTrustedDomainCache@0
+RpcServerListen@12
+LsaQueryInformationPolicy@12
+LsapRmInitializeServer@0
+LsapDbInitializeUnicodeNames@0
+SamrCloseHandle@4
+SamrOpenDomain@16
+LsapAdtInitializeCrashOnFail@0
+LdrGetProcedureAddress@16
+LsapDbAcquireWriteLockTrustedDomainList@4
+LsapLoadPackage@8
+LsapAuApiDispatchLookupPackage@8
+LsapAuMspInitialize@0
+LsapRpcCopySid@12
+LsapGetMessageStrings@20
+LsapDbEnableReplicatorNotification@0
+RpcServerUseProtseqEpW@16
+LsapDbNotifyChangeObject@8
+LsapGetAccountDomainInfo@4
+_alloca_probe
diff --git a/private/lsa/server/lsasrvmm.c b/private/lsa/server/lsasrvmm.c
new file mode 100644
index 000000000..86de05763
--- /dev/null
+++ b/private/lsa/server/lsasrvmm.c
@@ -0,0 +1,707 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ lsasrvmm.c
+
+Abstract:
+
+ This module provides LSA Server Memory Management including the following
+
+ - Heap allocation and free routines
+ - Free List Management.
+ - RPC memory copy routines
+
+Author:
+
+ Jim Kelly JimK February 26, 1991
+ Scott Birrell ScottBi February 29, 1992
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+
+
+BOOLEAN
+LsapHeapInitialize()
+
+/*++
+
+Routine Description:
+
+ This function initializes the LSA heap.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Use the process heap for memory allocations.
+ //
+
+ LsapHeap = RtlProcessHeap();
+
+ return TRUE;
+
+}
+
+
+PVOID
+LsapAllocateLsaHeap (
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates heap space from the LSA heap.
+
+Arguments:
+
+ Length - the number of bytes of heap space to allocate.
+
+Return Value:
+
+ Non-Null - the allocation was successful.
+
+ NULL - the allocation failed.
+
+--*/
+
+{
+ return RtlAllocateHeap( LsapHeap, 0, Length );
+}
+
+
+VOID
+LsapFreeLsaHeap (
+ IN PVOID Base
+ )
+
+/*++
+
+Routine Description:
+
+ This function free previously allocated heap space.
+
+Arguments:
+
+ Base - Provides the address of the previously allocated heap
+ space to deallocate.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ RtlFreeHeap( LsapHeap, 0, Base );
+ return;
+}
+
+
+NTSTATUS
+LsapMmCreateFreeList(
+ OUT PLSAP_MM_FREE_LIST FreeList,
+ IN ULONG MaxEntries
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a Free List. The Free List structure is
+ initialized and, if a non-zero maximum entry count is
+ specified, an array of buffer entries is created.
+
+Arguments:
+
+ FreeList - Pointer to Free List structure to be initialized. It is
+ the caller's responsibility to provide memory for this structure.
+
+ MaxEntries - Specifies the maximum entries for the Free List.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call. In this case,
+ the Free List header is initialized with a zero count.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ FreeList->MaxCount = MaxEntries;
+ FreeList->UsedCount = 0;
+
+ if (MaxEntries > 0) {
+
+ FreeList->Buffers =
+ LsapAllocateLsaHeap(MaxEntries * sizeof(LSAP_MM_FREE_LIST_ENTRY));
+
+ if (FreeList->Buffers == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ FreeList->MaxCount = 0;
+ }
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsapMmAllocateMidl(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ OUT PVOID *BufferAddressLocation,
+ IN ULONG BufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates memory for a buffer via MIDL_user_allocate
+ and returns the resulting buffer address in a specified location.
+ The address of the allocated buffer is recorded in the Free List.
+
+Arguments:
+
+ FreeList - Optional pointer to Free List.
+
+ BufferAddressLocation - Pointer to location that will receive either the
+ address of the allocated buffer, or NULL.
+
+ BufferLength - Size of the buffer in bytes.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ such as memory to complete the call.
+
+--*/
+
+{
+ //
+ // If no Free List is specified, just allocate the memory.
+ //
+
+ if (FreeList == NULL) {
+
+ *BufferAddressLocation = MIDL_user_allocate(BufferLength);
+
+ if (*BufferAddressLocation != NULL) {
+
+ return(STATUS_SUCCESS);
+ }
+
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ //
+ // A Free List is specified.
+ //
+
+ if (FreeList->UsedCount < FreeList->MaxCount) {
+
+ *BufferAddressLocation = MIDL_user_allocate(BufferLength);
+
+ if (*BufferAddressLocation != NULL) {
+
+ FreeList->Buffers[FreeList->UsedCount].Buffer = *BufferAddressLocation;
+ FreeList->Buffers[FreeList->UsedCount].Options = LSAP_MM_MIDL;
+ FreeList->UsedCount++;
+ return(STATUS_SUCCESS);
+ }
+ }
+
+ *BufferAddressLocation = NULL;
+ return(STATUS_INSUFFICIENT_RESOURCES);
+}
+
+
+VOID
+LsapMmFreeLastEntry(
+ IN PLSAP_MM_FREE_LIST FreeList
+ )
+
+/*++
+
+Routine Description:
+
+ This function frees the last buffer appeended to the Free List.
+
+Arguments:
+
+ FreeList - Pointer to Free List.
+
+--*/
+
+{
+ ULONG LastIndex = FreeList->UsedCount - 1;
+
+ if (FreeList->Buffers[LastIndex].Options & LSAP_MM_MIDL) {
+
+ MIDL_user_free( FreeList->Buffers[LastIndex].Buffer );
+
+ } else {
+
+ LsapFreeLsaHeap( FreeList->Buffers[LastIndex].Buffer );
+ }
+
+ FreeList->Buffers[LastIndex].Buffer = NULL;
+ FreeList->UsedCount--;
+}
+
+VOID
+LsapMmCleanupFreeList(
+ IN PLSAP_MM_FREE_LIST FreeList,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function optionally frees up buffers on the specified Free List,
+ and disposes of the List buffer pointer array.
+
+Arguments:
+
+ FreeList - Pointer to Free List
+
+ Options - Specifies optional actions to be taken
+
+ LSAP_MM_FREE_BUFFERS - Free buffers on the list.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ULONG Index;
+ PVOID Buffer = NULL;
+
+ //
+ // If requested, free up the memory for each buffer on the list.
+ //
+
+ if (Options & LSAP_MM_FREE_BUFFERS) {
+
+ for (Index = 0; Index < FreeList->UsedCount; Index++) {
+
+ Buffer = FreeList->Buffers[Index].Buffer;
+
+ if (FreeList->Buffers[Index].Options & LSAP_MM_MIDL) {
+
+ MIDL_user_free(Buffer);
+ continue;
+ }
+
+ if (FreeList->Buffers[Index].Options & LSAP_MM_HEAP) {
+
+ LsapFreeLsaHeap(Buffer);
+ }
+ }
+ }
+
+ //
+ // Now dispose of the List buffer pointer array.
+ //
+
+ if (FreeList->MaxCount > 0) {
+
+ LsapFreeLsaHeap( FreeList->Buffers );
+ FreeList->Buffers = NULL;
+ }
+}
+
+
+NTSTATUS
+LsapRpcCopyUnicodeString(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString
+ )
+
+/*++
+
+Routine Description:
+
+ This function copies a Unicode String to an output string, allocating
+ memory for the output string's buffer via MIDL_user_allocate. The
+ buffer is recorded on the specified Free List (if any).
+
+Arguments:
+
+ FreeList - Optional pointer to Free List.
+
+ DestinationString - Pointer to Output Unicode String structure to
+ be initialized.
+
+ SourceString - Pointer to input string
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PWSTR Buffer = NULL;
+
+ //
+ // Copy the Unicode String structure.
+ //
+
+ *DestinationString = *SourceString;
+
+ //
+ // If there is no source string buffer, just return.
+ //
+
+ if (SourceString->Buffer == NULL) {
+
+ goto RpcCopyUnicodeStringFinish;
+ }
+
+ //
+ // If the source string is of NULL length, set the destination buffer
+ // to NULL.
+ //
+
+ if (SourceString->MaximumLength == 0) {
+
+ DestinationString->Buffer = NULL;
+ goto RpcCopyUnicodeStringFinish;
+ }
+
+ if (ARGUMENT_PRESENT(FreeList)) {
+
+ Status = LsapMmAllocateMidl(
+ FreeList,
+ (PVOID *) &DestinationString->Buffer,
+ SourceString->MaximumLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto RpcCopyUnicodeStringError;
+ }
+
+ } else {
+
+ DestinationString->Buffer =
+ MIDL_user_allocate( SourceString->MaximumLength );
+
+ if (DestinationString->Buffer == NULL) {
+
+ goto RpcCopyUnicodeStringError;
+ }
+ }
+
+ //
+ // Copy the source Unicode string over to the MIDL-allocated destination.
+ //
+
+ RtlCopyUnicodeString( DestinationString, SourceString );
+
+RpcCopyUnicodeStringFinish:
+
+ return(Status);
+
+RpcCopyUnicodeStringError:
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto RpcCopyUnicodeStringFinish;
+}
+
+
+NTSTATUS
+LsapRpcCopyUnicodeStrings(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ IN ULONG Count,
+ OUT PUNICODE_STRING *DestinationStrings,
+ IN PUNICODE_STRING SourceStrings
+ )
+
+/*++
+
+Routine Description:
+
+ This function constructs an array of Unicode strings in which the
+ memory for the array and the string buffers has been allocated via
+ MIDL_user_allocate(). It is called by server API workers to construct
+ output string arrays. Memory allocated can optionally be placed on
+ the caller's Free List (if any).
+
+Arguments:
+
+ FreeList - Optional pointer to Free List.
+
+ DestinationStrings - Receives a pointer to an initialized array of Count
+ Unicode String structures.
+
+ SourceStrings - Pointer to input array of Unicode String structures.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Index, FreeIndex;
+ PUNICODE_STRING OutputDestinationStrings = NULL;
+ ULONG OutputDestinationStringsLength;
+
+ if (Count == 0) {
+
+ goto CopyUnicodeStringsFinish;
+ }
+
+ //
+ // Allocate zero-filled memory for the array of Unicode String
+ // structures.
+ //
+
+ OutputDestinationStringsLength = Count * sizeof (UNICODE_STRING);
+ OutputDestinationStrings = MIDL_user_allocate( OutputDestinationStringsLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (OutputDestinationStrings == NULL) {
+
+ goto CopyUnicodeStringsError;
+ }
+
+ //
+ // Now copy each string, allocating memory via MIDL_user_allocate()
+ // for its buffer if non-NULL.
+ //
+
+ for (Index = 0; Index < Count; Index++) {
+
+ Status = LsapRpcCopyUnicodeString(
+ FreeList,
+ &OutputDestinationStrings[Index],
+ &SourceStrings[Index]
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CopyUnicodeStringsError;
+ }
+
+CopyUnicodeStringsFinish:
+
+ *DestinationStrings = OutputDestinationStrings;
+
+ return(Status);
+
+CopyUnicodeStringsError:
+
+ //
+ // If necessary, free up any Unicode string buffers allocated here.
+ //
+
+ for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) {
+
+ if (OutputDestinationStrings[ FreeIndex].Buffer != NULL) {
+
+ MIDL_user_free( &OutputDestinationStrings[ FreeIndex].Buffer );
+ }
+ }
+
+ //
+ // If necessary, free the buffer allocated to hold the array of
+ // Unicode string structures.
+ //
+
+ if (OutputDestinationStrings != NULL) {
+
+ MIDL_user_free( OutputDestinationStrings );
+ OutputDestinationStrings = NULL;
+ }
+
+ goto CopyUnicodeStringsFinish;
+}
+
+
+NTSTATUS
+LsapRpcCopySid(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ OUT PSID *DestinationSid,
+ IN PSID SourceSid
+ )
+
+/*++
+
+Routine Description:
+
+ This function makes a copy of a Sid in which memory is allocated
+ via MIDL user allocate. It is called to return Sids via RPC to
+ the client.
+
+Arguments:
+
+ FreeList - Optional pointer to Free List.
+
+ DestinationSid - Receives a pointer to the Sid copy.
+
+ SourceSid - Pointer to the Sid to be copied.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ ULONG SidLength = RtlLengthSid( SourceSid );
+
+ Status = LsapMmAllocateMidl(
+ FreeList,
+ DestinationSid,
+ SidLength
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ RtlCopyMemory( *DestinationSid, SourceSid, SidLength );
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+LsapRpcCopyTrustInformation(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ OUT PLSAPR_TRUST_INFORMATION OutputTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION InputTrustInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function makes a copy of a Trust Information structure in which
+ the Sid and Name buffer have been allocated individually by
+ MIDL_user_allocate. The function is used to generate output
+ Trust Information for RPC server API. Cleanup is the responsibility
+ of the caller.
+
+Arguments:
+
+ FreeList - Optional pointer to Free List.
+
+ OutputTrustInformation - Points to Trust Information structure to
+ be filled in. This structure has normally been allocated via
+ MIDL_user_allocate.
+
+ InputTrustInformation - Pointer to input Trust Information.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Copy the Name.
+ //
+
+ Status = LsapRpcCopyUnicodeString(
+ FreeList,
+ (PUNICODE_STRING) &OutputTrustInformation->Name,
+ (PUNICODE_STRING) &InputTrustInformation->Name
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CopyTrustInformationError;
+ }
+
+ //
+ // Copy the Sid.
+ //
+
+ Status = LsapRpcCopySid(
+ FreeList,
+ (PSID) &OutputTrustInformation->Sid,
+ (PSID) InputTrustInformation->Sid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CopyTrustInformationError;
+ }
+
+CopyTrustInformationFinish:
+
+ return(Status);
+
+CopyTrustInformationError:
+
+ goto CopyTrustInformationFinish;
+}
diff --git a/private/lsa/server/lsasrvmm.h b/private/lsa/server/lsasrvmm.h
new file mode 100644
index 000000000..e28b56832
--- /dev/null
+++ b/private/lsa/server/lsasrvmm.h
@@ -0,0 +1,125 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsasrvmm.h
+
+Abstract:
+
+ Local Security Authority - Main Include File for Lsa Server Memory
+ Management Routines.
+
+Author:
+
+ Scott Birrell (ScottBi) February 29, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Free List Routines and Definitions //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+#define LSAP_MM_MIDL ((ULONG) 0x00000001L)
+#define LSAP_MM_HEAP ((ULONG) 0x00000002L)
+
+//
+// Options from LsapMmCleanupFreeList
+//
+
+#define LSAP_MM_FREE_BUFFERS ((ULONG) 0x00000001L)
+
+typedef struct _LSAP_MM_FREE_LIST_ENTRY {
+
+ PVOID Buffer;
+ ULONG Options;
+
+} LSAP_MM_FREE_LIST_ENTRY, *PLSAP_MM_FREE_LIST_ENTRY;
+
+typedef struct _LSAP_MM_FREE_LIST {
+
+ ULONG UsedCount;
+ ULONG MaxCount;
+ PLSAP_MM_FREE_LIST_ENTRY Buffers;
+
+} LSAP_MM_FREE_LIST, *PLSAP_MM_FREE_LIST;
+
+NTSTATUS
+LsapMmCreateFreeList(
+ OUT PLSAP_MM_FREE_LIST FreeList,
+ IN ULONG MaxEntries
+ );
+
+NTSTATUS
+LsapMmAllocateMidl(
+ IN PLSAP_MM_FREE_LIST FreeList,
+ OUT PVOID *OutputBuffer,
+ IN ULONG BufferLength
+ );
+
+VOID
+LsapMmFreeLastEntry(
+ IN PLSAP_MM_FREE_LIST FreeList
+ );
+
+VOID
+LsapMmCleanupFreeList(
+ IN PLSAP_MM_FREE_LIST FreeList,
+ IN ULONG Options
+ );
+
+NTSTATUS
+LsapRpcCopyUnicodeString(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString
+ );
+
+NTSTATUS
+LsapRpcCopyUnicodeStrings(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ IN ULONG Count,
+ OUT PUNICODE_STRING *DestinationStrings,
+ IN PUNICODE_STRING SourceStrings
+ );
+
+NTSTATUS
+LsapRpcCopySid(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ OUT PSID *DestinationSid,
+ IN PSID SourceSid
+ );
+
+NTSTATUS
+LsapRpcCopyTrustInformation(
+ IN OPTIONAL PLSAP_MM_FREE_LIST FreeList,
+ OUT PLSAPR_TRUST_INFORMATION OutputTrustInformation,
+ IN PLSAPR_TRUST_INFORMATION InputTrustInformation
+ );
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// Heap Routines //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+LsapHeapInitialize();
+
+PVOID
+LsapAllocateLsaHeap (
+ IN ULONG Length
+ );
+
+VOID
+LsapFreeLsaHeap (
+ IN PVOID Base
+ );
diff --git a/private/lsa/server/lsasrvp.h b/private/lsa/server/lsasrvp.h
new file mode 100644
index 000000000..0c943f5b9
--- /dev/null
+++ b/private/lsa/server/lsasrvp.h
@@ -0,0 +1,496 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsasrvp.h
+
+Abstract:
+
+ LSA Subsystem - Private Includes for Server Side
+
+ This file contains includes that are global to the Lsa Server Side
+
+Author:
+
+ Scott Birrell (ScottBi) January 22, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LSASRVP_
+#define _LSASRVP_
+
+//
+// The LSA Server Is UNICODE Based. Define UNICODE before global includes
+// so that it is defined before the TEXT macro.
+//
+
+#ifndef UNICODE
+
+#define UNICODE
+
+#endif // UNICODE
+
+#include <lsacomp.h>
+
+
+//
+// The following come from \nt\private\inc
+
+#include <samrpc.h>
+#include <samsrv.h>
+#include <samisrv.h>
+#include <lsarpc.h>
+#include <lsaisrv.h>
+#include <nlrepl.h>
+#include <seposix.h>
+
+//
+// The following all come from \nt\private\lsa\server
+//
+
+#include "lsasrvmm.h"
+#include "au.h"
+#include "db.h"
+#include "adt.h"
+#include "dblookup.h"
+
+
+
+//////////////////////////////////////////////////////////////////////
+// //
+// The following define controls the diagnostic capabilities that //
+// are built into LSA. //
+// //
+//////////////////////////////////////////////////////////////////////
+
+#if DBG
+#define LSAP_DIAGNOSTICS 1
+#endif // DBG
+
+//
+// BUGBUG: remove when shipping. This is to track the closing of the
+// LsapDbHandle
+//
+
+// #define TRACK_HANDLE_CLOSE 1
+
+//
+// These definitions are useful diagnostics aids
+//
+
+#if LSAP_DIAGNOSTICS
+
+//
+// Diagnostics included in build
+//
+
+//
+// Test for diagnostics enabled
+//
+
+#define IF_LSAP_GLOBAL( FlagName ) \
+ if (LsapGlobalFlag & (LSAP_DIAG_##FlagName))
+
+//
+// Diagnostics print statement
+//
+
+#define LsapDiagPrint( FlagName, _Text_ ) \
+ IF_LSAP_GLOBAL( FlagName ) \
+ DbgPrint _Text_
+
+
+
+
+#else
+
+//
+// No diagnostics included in build
+//
+
+//
+// Test for diagnostics enabled
+//
+
+#define IF_LSAP_GLOBAL( FlagName ) if (FALSE)
+
+
+//
+// Diagnostics print statement (nothing)
+//
+
+#define LsapDiagPrint( FlagName, Text ) ;
+
+
+#endif // LSAP_DIAGNOSTICS
+
+
+//
+// The following flags enable or disable various diagnostic
+// capabilities within LSA. These flags are set in
+// LsapGlobalFlag
+//
+// DB_LOOKUP_WORK_LIST - Display activities related to sid/name lookups.
+//
+// AU_TRACK_THREADS - Display dynamic AU thread creation / deletion
+// information.
+//
+// AU_MESSAGES - Display information related to the processing of
+// Authentication messages.
+//
+// AU_LOGON_SESSIONS - Display information about the creation/deletion
+// of logon sessions within LSA.
+//
+// DB_INIT - Display information about the initialization of LSA.
+//
+
+#define LSAP_DIAG_DB_LOOKUP_WORK_LIST ((ULONG) 0x00000001L)
+#define LSAP_DIAG_AU_TRACK_THREADS ((ULONG) 0x00000002L)
+#define LSAP_DIAG_AU_MESSAGES ((ULONG) 0x00000004L)
+#define LSAP_DIAG_AU_LOGON_SESSIONS ((ULONG) 0x00000008L)
+#define LSAP_DIAG_DB_INIT ((ULONG) 0x00000010L)
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////
+// //
+// Other defines //
+// //
+//////////////////////////////////////////////////////////////////////
+
+//
+// Internal limit on the number of SIDs that can be assigned to a single
+// security context. If, for some reason, someone logs on to an account
+// and is assigned more than this number of SIDs, the logon will fail.
+// An error should be logged in this case.
+//
+
+#define LSAP_CONTEXT_SID_LIMIT 1000
+
+
+//
+// Heap available for general use throughout LSA
+//
+
+PVOID LsapHeap;
+
+//
+// LSA Private Global State Data Structure
+//
+
+typedef struct _LSAP_STATE {
+
+ HANDLE LsaCommandPortHandle;
+ HANDLE RmCommandPortHandle;
+ HANDLE AuditLogFileHandle;
+ HANDLE AuditLogSectionHandle;
+ PVOID AuditLogBaseAddress;
+ ULONG AuditLogViewSize;
+ LARGE_INTEGER AuditLogInitSize;
+ LARGE_INTEGER AuditLogMaximumSizeOfSection;
+ OBJECT_ATTRIBUTES AuditLogObjectAttributes;
+ STRING AuditLogNameString;
+ GENERIC_MAPPING GenericMapping;
+ UNICODE_STRING SubsystemName;
+ PRIVILEGE_SET Privileges;
+ BOOLEAN GenerateOnClose;
+ BOOLEAN SystemShutdownPending;
+
+} LSAP_STATE, *PLSAP_STATE;
+
+extern LSAP_STATE LsapState;
+
+extern BOOLEAN LsapInitialized;
+
+//
+// Global handle to LSA's policy object.
+// This handle is opened for trusted client.
+//
+
+extern LSAPR_HANDLE LsapPolicyHandle;
+
+//
+// LSA Server Command Dispatch Table Entry
+//
+
+typedef NTSTATUS (*PLSA_COMMAND_WORKER)(PLSA_COMMAND_MESSAGE, PLSA_REPLY_MESSAGE);
+
+//
+// LSA Client Control Block
+//
+// This structure contains context information relevant to a successful
+// LsaOpenLsa call.
+//
+
+typedef struct _LSAP_CLIENT_CONTROL_BLOCK {
+ HANDLE KeyHandle; // Configuration Registry Key
+ ACCESS_MASK GrantedAccess; // Accesses granted to LSA Database Object
+} LSAP_CLIENT_CONTROL_BLOCK, *PLSAP_CLIENT_CONTROL_BLOCK;
+
+
+//
+// LSA Privilege Pseudo-Object Types and Flags
+//
+
+// *********************** IMPORTANT NOTE ************************
+//
+// Privilege objects (privileges containing a list of users who have that
+// privilge) are pseudo-objects that use the account objects as a backing
+// stored. There are currently no public interfaces to open a privilege
+// object, so there need not be public access flags.
+//
+
+#define PRIVILEGE_VIEW 0x00000001L
+#define PRIVILEGE_ADJUST 0x00000002L
+#define PRIVILEGE_ALL (STANDARD_RIGHTS_REQUIRED | \
+ PRIVILEGE_VIEW | \
+ PRIVILEGE_ADJUST)
+
+
+
+//
+// LSA API Error Handling Cleanup Flags
+//
+// These flags specify cleanup operations to be performed after an LSA
+// API call has hit a fatal error. They are passed in the ErrorCleanupFlags
+// variable of the API or worker's error handling routine.
+//
+
+#define LSAP_CLEANUP_REVERT_TO_SELF (0x00000001L)
+#define LSAP_CLEANUP_CLOSE_LSA_HANDLE (0x00000002L)
+#define LSAP_CLEANUP_FREE_USTRING (0x00000004L)
+#define LSAP_CLEANUP_CLOSE_REG_KEY (0x00000008L)
+#define LSAP_CLEANUP_DELETE_REG_KEY (0x00000010L)
+#define LSAP_CLEANUP_DB_UNLOCK (0x00000020L)
+
+BOOLEAN
+LsapRmInitializeServer(
+ );
+
+VOID
+LsapRmServerThread(
+ );
+
+NTSTATUS
+LsapRPCInit(
+ );
+
+BOOLEAN
+LsapAuInit( // Authentication initialization
+ );
+
+NTSTATUS
+LsapDbInitializeRights(
+ );
+
+VOID
+LsapDbCleanupRights(
+ );
+
+NTSTATUS
+LsapCallRm(
+ IN RM_COMMAND_NUMBER CommandNumber,
+ IN OPTIONAL PVOID CommandParams,
+ IN ULONG CommandParamsLength,
+ OUT OPTIONAL PVOID ReplyBuffer,
+ IN ULONG ReplyBufferLength
+ );
+
+NTSTATUS
+LsapLogonSessionDeletedWrkr(
+ IN PLSA_COMMAND_MESSAGE CommandMessage,
+ OUT PLSA_REPLY_MESSAGE ReplyMessage
+ );
+
+NTSTATUS
+LsapComponentTestWrkr(
+ IN PLSA_COMMAND_MESSAGE CommandMessage,
+ OUT PLSA_REPLY_MESSAGE ReplyMessage
+ );
+
+
+//
+// Prototypes of RPC free routines used by LsaIFree.c
+//
+
+void _fgs__STRING (STRING * _source);
+void _fgs__LSAPR_SID_INFORMATION (LSAPR_SID_INFORMATION * _source);
+void _fgs__LSAPR_SID_ENUM_BUFFER (LSAPR_SID_ENUM_BUFFER * _source);
+void _fgs__LSAPR_ACCOUNT_INFORMATION (LSAPR_ACCOUNT_INFORMATION * _source);
+void _fgs__LSAPR_ACCOUNT_ENUM_BUFFER (LSAPR_ACCOUNT_ENUM_BUFFER * _source);
+void _fgs__LSAPR_UNICODE_STRING (LSAPR_UNICODE_STRING * _source);
+void _fgs__LSAPR_SECURITY_DESCRIPTOR (LSAPR_SECURITY_DESCRIPTOR * _source);
+void _fgs__LSAPR_SR_SECURITY_DESCRIPTOR (LSAPR_SR_SECURITY_DESCRIPTOR * _source);
+void _fgs__LSAPR_POLICY_PRIVILEGE_DEF (LSAPR_POLICY_PRIVILEGE_DEF * _source);
+void _fgs__LSAPR_PRIVILEGE_ENUM_BUFFER (LSAPR_PRIVILEGE_ENUM_BUFFER * _source);
+void _fgs__LSAPR_OBJECT_ATTRIBUTES (LSAPR_OBJECT_ATTRIBUTES * _source);
+void _fgs__LSAPR_CR_CIPHER_VALUE (LSAPR_CR_CIPHER_VALUE * _source);
+void _fgs__LSAPR_TRUST_INFORMATION (LSAPR_TRUST_INFORMATION * _source);
+void _fgs__LSAPR_TRUSTED_ENUM_BUFFER (LSAPR_TRUSTED_ENUM_BUFFER * _source);
+void _fgs__LSAPR_REFERENCED_DOMAIN_LIST (LSAPR_REFERENCED_DOMAIN_LIST * _source);
+void _fgs__LSAPR_TRANSLATED_SIDS (LSAPR_TRANSLATED_SIDS * _source);
+void _fgs__LSAPR_TRANSLATED_NAME (LSAPR_TRANSLATED_NAME * _source);
+void _fgs__LSAPR_TRANSLATED_NAMES (LSAPR_TRANSLATED_NAMES * _source);
+void _fgs__LSAPR_POLICY_ACCOUNT_DOM_INFO (LSAPR_POLICY_ACCOUNT_DOM_INFO * _source);
+void _fgs__LSAPR_POLICY_PRIMARY_DOM_INFO (LSAPR_POLICY_PRIMARY_DOM_INFO * _source);
+void _fgs__LSAPR_POLICY_PD_ACCOUNT_INFO (LSAPR_POLICY_PD_ACCOUNT_INFO * _source);
+void _fgs__LSAPR_POLICY_REPLICA_SRCE_INFO (LSAPR_POLICY_REPLICA_SRCE_INFO * _source);
+void _fgs__LSAPR_POLICY_AUDIT_EVENTS_INFO (LSAPR_POLICY_AUDIT_EVENTS_INFO * _source);
+void _fgs__LSAPR_TRUSTED_DOMAIN_NAME_INFO (LSAPR_TRUSTED_DOMAIN_NAME_INFO * _source);
+void _fgs__LSAPR_TRUSTED_CONTROLLERS_INFO (LSAPR_TRUSTED_CONTROLLERS_INFO * _source);
+void _fgu__LSAPR_POLICY_INFORMATION (LSAPR_POLICY_INFORMATION * _source, POLICY_INFORMATION_CLASS _branch);
+void _fgu__LSAPR_TRUSTED_DOMAIN_INFO (LSAPR_TRUSTED_DOMAIN_INFO * _source, TRUSTED_INFORMATION_CLASS _branch);
+
+//
+// Old worker prototypes - These are temporary
+//
+
+#define LsapComponentTestCommandWrkr LsapComponentTestWrkr
+#define LsapWriteAuditMessageCommandWrkr LsapAdtWriteLogWrkr
+
+NTSTATUS
+ServiceInit (
+ );
+
+NTSTATUS
+LsapInitLsa(
+ );
+
+BOOLEAN
+LsapSeSetWellKnownValues(
+ );
+
+NTSTATUS
+LsapRtlConvertSidToUnicodeString(
+ IN PSID Sid,
+ OUT PUNICODE_STRING UnicodeString
+ );
+
+VOID
+RtlConvertSidToText(
+ IN PSID Sid,
+ OUT PUCHAR Buffer
+ );
+
+ULONG
+RtlSizeANSISid(
+ IN PSID Sid
+ );
+
+NTSTATUS
+LsapRtlCopyUnicodeString(
+ OUT PUNICODE_STRING OutputString,
+ IN PUNICODE_STRING InputString,
+ BOOLEAN AllocateMemory
+ );
+
+
+NTSTATUS
+LsapGetMessageStrings(
+ LPVOID Resource,
+ DWORD Index1,
+ PUNICODE_STRING String1,
+ DWORD Index2,
+ PUNICODE_STRING String2 OPTIONAL
+ );
+
+
+VOID
+LsapLogError(
+ IN OPTIONAL PUCHAR Message,
+ IN NTSTATUS Status
+ );
+
+
+NTSTATUS
+LsapGetPrivilegesAndQuotas(
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN ULONG IdCount,
+ IN PSID_AND_ATTRIBUTES Ids,
+ OUT PULONG PrivilegeCount,
+ OUT PLUID_AND_ATTRIBUTES *Privileges,
+ OUT PQUOTA_LIMITS QuotaLimits
+ );
+
+
+NTSTATUS
+LsapQueryClientInfo(
+ PTOKEN_USER *UserSid,
+ PLUID AuthenticationId
+ );
+
+
+NTSTATUS
+LsapGetAccountDomainInfo(
+ PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo
+ );
+
+NTSTATUS
+LsapOpenSam( VOID );
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Shared Global Variables //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// Handles used to talk to SAM directly.
+// Also, a flag to indicate whether or not the handles are valid.
+//
+
+
+extern BOOLEAN LsapSamOpened;
+
+extern SAMPR_HANDLE LsapAccountDomainHandle;
+extern SAMPR_HANDLE LsapBuiltinDomainHandle;
+
+
+
+//
+// These variables are used to control the number of
+// threads used for processing Logon Process calls.
+// See auloop.c for a description of these variables.
+//
+
+extern RTL_RESOURCE LsapAuThreadCountLock;
+extern LONG LsapAuActiveThreads;
+extern LONG LsapAuFreeThreads;
+extern LONG LsapAuFreeThreadsGoal;
+extern LONG LsapAuMinimumThreads;
+extern LONG LsapAuMaximumThreads;
+extern LONG LsapAuCallsToProcess;
+
+
+#if DBG
+
+//
+// Used to extend the resource timeout for lsass.exe to
+// prevent it from hitting deadlock warnings in stress tests.
+//
+
+extern IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used;
+#endif \\DBG
+
+
+#if LSAP_DIAGNOSTICS
+
+//
+// Used as a global diagnostics control flag within lsass.exe
+//
+
+extern ULONG LsapGlobalFlag;
+#endif // LSAP_DIAGNOSTICS
+
+
+#endif // _LSASRVP_
diff --git a/private/lsa/server/lsass.c b/private/lsa/server/lsass.c
new file mode 100644
index 000000000..b3a8dcde9
--- /dev/null
+++ b/private/lsa/server/lsass.c
@@ -0,0 +1,543 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsass.c
+
+Abstract:
+
+ Local Security Authority Subsystem - Main Program.
+
+Author:
+
+ Scott Birrell (ScottBi) Mar 12, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "ntrpcp.h"
+#include "lmcons.h"
+#include "lmalert.h"
+#include "alertmsg.h"
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Shared Global Variables //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+#if DBG
+
+IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = {
+ 0, // Reserved
+ 0, // Reserved
+ 0, // Reserved
+ 0, // Reserved
+ 0, // GlobalFlagsClear
+ 0, // GlobalFlagsSet
+ 900000, // CriticalSectionTimeout (milliseconds)
+ 0, // DeCommitFreeBlockThreshold
+ 0, // DeCommitTotalFreeThreshold
+ 0, // LockPrefixTable
+ 0, 0, 0, 0, 0, 0, 0 // Reserved
+};
+
+
+#endif \\DBG
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Internal routine prototypes //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+VOID
+LsapNotifyInitializationFinish(
+ IN NTSTATUS CompletionStatus
+ );
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+DWORD WINAPI
+LsaTopLevelExceptionHandler(
+ struct _EXCEPTION_POINTERS *ExceptionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ The Top Level exception filter for lsass.exe.
+
+ This ensures the entire process will be cleaned up if any of
+ the threads fail. Since lsass.exe is a distributed application,
+ it is better to fail the entire process than allow random threads
+ to continue executing.
+
+Arguments:
+
+ ExceptionInfo - Identifies the exception that occurred.
+
+
+Return Values:
+
+ EXCEPTION_EXECUTE_HANDLER - Terminate the process.
+
+ EXCEPTION_CONTINUE_SEARCH - Continue processing as though this filter
+ was never called.
+
+
+--*/
+{
+ HANDLE hModule;
+
+
+ //
+ // Raise an alert
+ //
+
+ hModule = LoadLibraryA("netapi32");
+
+ if ( hModule != NULL ) {
+ NET_API_STATUS (NET_API_FUNCTION *NetAlertRaiseExFunction)
+ (LPTSTR, LPVOID, DWORD, LPTSTR);
+
+
+ NetAlertRaiseExFunction =
+ (NET_API_STATUS (NET_API_FUNCTION *)(LPTSTR, LPVOID, DWORD, LPTSTR))
+ GetProcAddress(hModule, "NetAlertRaiseEx");
+
+ if ( NetAlertRaiseExFunction != NULL ) {
+ NTSTATUS Status;
+ UNICODE_STRING Strings;
+
+ char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)];
+ PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message;
+
+ //
+ // Build the variable data
+ //
+
+ admin->alrtad_errcode = ALERT_UnhandledException;
+ admin->alrtad_numstrings = 0;
+
+ Strings.Buffer = (LPWSTR) ALERT_VAR_DATA(admin);
+ Strings.Length = 0;
+ Strings.MaximumLength = ALERTSZ;
+
+ Status = RtlIntegerToUnicodeString(
+ (ULONG)ExceptionInfo->ExceptionRecord->ExceptionCode,
+ 16,
+ &Strings );
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Length += sizeof(WCHAR);
+
+ Status = RtlAppendUnicodeToString(
+ &Strings,
+ L"LSASS" );
+ }
+
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
+ Strings.MaximumLength -= Strings.Length + sizeof(WCHAR);
+ Strings.Length = 0;
+
+ Status = RtlIntegerToUnicodeString(
+ (ULONG)ExceptionInfo->ExceptionRecord->ExceptionAddress,
+ 16,
+ &Strings );
+ }
+
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
+
+ (VOID) (*NetAlertRaiseExFunction)(
+ ALERT_ADMIN_EVENT,
+ message,
+ (DWORD)((PCHAR)Strings.Buffer -
+ (PCHAR)message),
+ L"LSASS" );
+ }
+
+ }
+
+
+ }
+
+ (VOID) FreeLibrary( hModule );
+ }
+
+
+ //
+ // Just continue processing the exception.
+ //
+
+ return EXCEPTION_CONTINUE_SEARCH;
+
+}
+
+
+
+VOID _CRTAPI1
+main ()
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN InitializationNotified = FALSE;
+ KPRIORITY BasePriority;
+ BOOLEAN EnableAlignmentFaults = TRUE;
+
+ //
+ // Define a top-level exception handler for the entire process.
+ //
+
+ (VOID) SetErrorMode( SEM_FAILCRITICALERRORS );
+
+ (VOID) SetUnhandledExceptionFilter( &LsaTopLevelExceptionHandler );
+
+ //
+ // Turn on alignment fault fixups. This is necessary because
+ // several structures stored in the registry have qword aligned
+ // fields. They are nicely aligned in our structures, but they
+ // end up being forced out of alignment when being stored because
+ // the registry api require data to be passed following a wierd
+ // length header.
+ //
+
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessEnableAlignmentFaultFixup,
+ (PVOID) &EnableAlignmentFaults,
+ sizeof(BOOLEAN)
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Run the LSA in the foreground.
+ //
+ // Several processes which depend on the LSA (like the lanman server)
+ // run in the foreground. If we don't run in the foreground, they'll
+ // starve waiting for us.
+ //
+
+ BasePriority = FOREGROUND_BASE_PRIORITY;
+
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessBasePriority,
+ &BasePriority,
+ sizeof(BasePriority)
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Do the following:
+ //
+ // Initialize LSA Pass-1
+ // This starts the RPC server.
+ // Does not do product type-specific initialization.
+ //
+ // Pause for installation if necessary
+ // Allows product type-specific information to be
+ // collected.
+ //
+ // Initialize the service-controller service
+ // dispatcher.
+ //
+ // Initialize LSA Pass-2
+ // Product type-specific initialization.
+ //
+ // Initialize SAM
+ //
+
+
+ //
+ // Initialize the LSA.
+ // If unsuccessful, we must exit with status so that the SM knows
+ // something has gone wrong.
+ //
+
+ Status = LsapInitLsa();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // Initialize the service dispatcher.
+ //
+ // We initialize the service dispatcher before the sam
+ // service is started. This will make the service controller
+ // start successfully even if SAM takes a long time to initialize.
+ //
+
+ Status = ServiceInit();
+
+ if (!NT_SUCCESS(Status) ) {
+
+ goto Cleanup;
+ }
+
+ //
+ // Initialize SAM
+ //
+
+ Status = SamIInitialize();
+
+ if (!NT_SUCCESS(Status) ) {
+
+ goto Cleanup;
+ }
+
+ LsapNotifyInitializationFinish(Status);
+
+ InitializationNotified = TRUE;
+
+ //
+ // Open a Trusted Handle to the local SAM server.
+ //
+
+ Status = LsapAuOpenSam();
+
+ if (!NT_SUCCESS(Status) ) {
+
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if (!InitializationNotified) {
+
+ LsapNotifyInitializationFinish(Status);
+ InitializationNotified = TRUE;
+ }
+
+ NtTerminateThread( NtCurrentThread(), Status );
+}
+
+VOID
+LsapNotifyInitializationFinish(
+ IN NTSTATUS CompletionStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function handles the notification of successful or
+ unsuccessful completion of initialization of the Security Process
+ lsass.exe. If initialization was unsuccessful, a popup appears. If
+ setup was run, one of two events is set. The SAM_SERVICE_STARTED event
+ is set if LSA and SAM started OK and the SETUP_FAILED event is set if LSA
+ or SAM server setup failed. Setup waits multiple on this object pair so
+ that it can detect either event being set and notify the user if necessary
+ that setup failed.
+
+Arguments:
+
+ CompletionStatus - Contains a standard Nt Result Code specifying
+ the success or otherwise of the initialization/installation.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Response;
+ UNICODE_STRING EventName;
+ OBJECT_ATTRIBUTES EventAttributes;
+ HANDLE EventHandle = NULL;
+
+ if (NT_SUCCESS(CompletionStatus)) {
+
+ //
+ // Set an event telling anyone wanting to call SAM that we're initialized.
+ //
+
+ RtlInitUnicodeString( &EventName, L"\\SAM_SERVICE_STARTED");
+ InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
+
+ Status = NtCreateEvent(
+ &EventHandle,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes,
+ NotificationEvent,
+ FALSE // The event is initially not signaled
+ );
+
+
+ if ( !NT_SUCCESS(Status)) {
+
+ //
+ // If the event already exists, a waiting thread beat us to
+ // creating it. Just open it.
+ //
+
+ if( Status == STATUS_OBJECT_NAME_EXISTS ||
+ Status == STATUS_OBJECT_NAME_COLLISION ) {
+
+ Status = NtOpenEvent(
+ &EventHandle,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes
+ );
+ }
+
+ if ( !NT_SUCCESS(Status)) {
+
+ KdPrint(("SAMSS: Failed to open SAM_SERVICE_STARTED event. %lX\n",
+ Status ));
+ KdPrint((" Failing to initialize SAM Server.\n"));
+ goto InitializationFinishError;
+ }
+ }
+
+ //
+ // Set the SAM_SERVICE_STARTED event. Except when an error occurs,
+ // don't close the event. Closing it would delete the event and
+ // a future waiter would never see it be set.
+ //
+
+ Status = NtSetEvent( EventHandle, NULL );
+
+ if ( !NT_SUCCESS(Status)) {
+
+ KdPrint(("SAMSS: Failed to set SAM_SERVICE_STARTED event. %lX\n",
+ Status ));
+ KdPrint((" Failing to initialize SAM Server.\n"));
+ NtClose(EventHandle);
+ goto InitializationFinishError;
+ }
+
+ } else {
+
+ //
+ // The initialization/installation of Lsa and/or SAM failed. Handle errors returned
+ // from the initialization/installation of LSA or SAM. Issue a popup
+ // and, if installing, set an event so that setup will continue and
+ // clean up.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ Status = NtRaiseHardError(
+ Status,
+ 0,
+ 0,
+ NULL,
+ OptionOk,
+ &Response
+ );
+
+ //
+ // Once the user has clicked OK in response to the popup, we come
+ // back to here. Set the event SETUP_FAILED. The Setup
+ // program (if running) waits multiple on the SAM_SERVICE_STARTED
+ // and SETUP_FAILED events.
+ //
+
+ //
+ // If setup.exe was run, signal the SETUP_FAILED event. setup.exe
+ // waits multiple on the SAM_SERVICE_STARTED and SETUP_FAILED events
+ // so setup will resume and cleanup/continue as appropriate if
+ // either of these events are set.
+ //
+
+ if (LsaISetupWasRun()) {
+
+ RtlInitUnicodeString( &EventName, L"\\SETUP_FAILED");
+ InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
+
+ //
+ // Open the SETUP_FAILED event (exists if setup.exe is running).
+ //
+
+ Status = NtOpenEvent(
+ &EventHandle,
+ SYNCHRONIZE|EVENT_MODIFY_STATE,
+ &EventAttributes
+ );
+
+ if ( !NT_SUCCESS(Status)) {
+
+ //
+ // Something is inconsistent. We know that setup was run
+ // so the event should exist.
+ //
+
+ KdPrint(("LSA Server: Failed to open SETUP_FAILED event. %lX\n",
+ Status ));
+ KdPrint((" Failing to initialize Lsa Server.\n"));
+ goto InitializationFinishError;
+ }
+
+ Status = NtSetEvent( EventHandle, NULL );
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto InitializationFinishError;
+ }
+
+InitializationFinishFinish:
+
+ return;
+
+InitializationFinishError:
+
+ goto InitializationFinishFinish;
+}
diff --git a/private/lsa/server/lsawrap.c b/private/lsa/server/lsawrap.c
new file mode 100644
index 000000000..2b51e7cec
--- /dev/null
+++ b/private/lsa/server/lsawrap.c
@@ -0,0 +1,2769 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsawrap.c
+
+Abstract:
+
+ LSA - Database - wrapper APIs for secret, trusted domain, and account
+ objects.
+
+ NOTE: This module should remain as portable code that is independent
+ of the implementation of the LSA Database. As such, it is
+ permitted to use only the exported LSA Database interfaces
+ contained in db.h and NOT the private implementation
+ dependent functions in dbp.h.
+
+Author:
+
+ Mike Swift (MikeSw) December 12, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+#include "dbp.h"
+#include <lmcons.h> // required by logonmsv.h
+#include <logonmsv.h> // SSI_SECRET_PREFIX...
+
+//
+// This structure holds the user right to system access mapping
+//
+
+typedef struct _LSAP_DB_RIGHT_AND_ACCESS {
+ UNICODE_STRING UserRight;
+ ULONG SystemAccess;
+} LSAP_DB_RIGHT_AND_ACCESS, *PLSAP_DB_RIGHT_AND_ACCESS;
+
+#define LSAP_DB_SYSTEM_ACCESS_TYPES 4
+
+LSAP_DB_RIGHT_AND_ACCESS LsapDbRightAndAccess[LSAP_DB_SYSTEM_ACCESS_TYPES];
+
+PSECURITY_DESCRIPTOR UserRightSD;
+
+UNICODE_STRING UserRightTypeName;
+
+GENERIC_MAPPING UserRightGenericMapping;
+
+
+
+NTSTATUS
+LsapDbInitializeRights(
+ )
+{
+/*++
+
+Routine Description:
+
+ Initializes global data for the new APIs handling user rights
+
+Arguments:
+
+ None
+
+Return Value:
+ STATUS_INSUFFICIENT_MEMORY - not enough memory to initialize the
+ data structures.
+
+--*/
+
+ SECURITY_DESCRIPTOR AbsoluteDescriptor;
+ ULONG DaclLength;
+ NTSTATUS Status;
+ PACL Dacl = NULL;
+ HANDLE LsaProcessTokenHandle = NULL;
+
+ //
+ // Interactive logons
+ //
+
+ RtlInitUnicodeString(
+ &LsapDbRightAndAccess[0].UserRight,
+ SE_INTERACTIVE_LOGON_NAME
+ );
+ LsapDbRightAndAccess[0].SystemAccess = SECURITY_ACCESS_INTERACTIVE_LOGON;
+
+ //
+ // network logons
+ //
+
+ RtlInitUnicodeString(
+ &LsapDbRightAndAccess[1].UserRight,
+ SE_NETWORK_LOGON_NAME
+ );
+ LsapDbRightAndAccess[1].SystemAccess = SECURITY_ACCESS_NETWORK_LOGON;
+
+ //
+ // SERVICE logons
+ //
+
+ RtlInitUnicodeString(
+ &LsapDbRightAndAccess[2].UserRight,
+ SE_SERVICE_LOGON_NAME
+ );
+ LsapDbRightAndAccess[2].SystemAccess = SECURITY_ACCESS_SERVICE_LOGON;
+
+ //
+ // BATCH logons
+ //
+
+ RtlInitUnicodeString(
+ &LsapDbRightAndAccess[3].UserRight,
+ SE_BATCH_LOGON_NAME
+ );
+ LsapDbRightAndAccess[3].SystemAccess = SECURITY_ACCESS_BATCH_LOGON;
+
+ //
+ // Create the security descriptor for the rights pseudo-object
+ //
+
+ //
+ // The ACL looks like this:
+ //
+ // Admins - PRIVILEGE_VIEW | PRIVILEGE_ADJUST
+ //
+
+ Status = RtlCreateSecurityDescriptor(
+ &AbsoluteDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ DaclLength = sizeof(ACL) -
+ sizeof(ULONG) + // for dummy in structure
+ sizeof(ACCESS_ALLOWED_ACE) +
+ RtlLengthSid(LsapAliasAdminsSid);
+
+ Dacl = (PACL) LsapAllocateLsaHeap(DaclLength);
+ if (Dacl == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ Status = RtlCreateAcl(
+ Dacl,
+ DaclLength,
+ ACL_REVISION
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Now add the access allowed ace for Admins. They are granted
+ // PRIVILEGE_VIEW and PRIVILEGE_ADJUST access. For now,
+ // PRIVILEGE_ADJUST is unused (since you can't add accounts to a
+ // privilege).
+ //
+ // ********* NOTE *************
+ //
+ // If real privilege objects are ever implemented, this should be moved
+ // to dbinit.c where the other LSA objects are created, and added to
+ // the table of real LSA objects.
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ Dacl,
+ ACL_REVISION,
+ PRIVILEGE_VIEW | PRIVILEGE_ADJUST,
+ LsapAliasAdminsSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = RtlSetOwnerSecurityDescriptor(
+ &AbsoluteDescriptor,
+ LsapAliasAdminsSid,
+ FALSE // owner not defaulted
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = RtlSetDaclSecurityDescriptor(
+ &AbsoluteDescriptor,
+ TRUE, // DACL present
+ Dacl,
+ FALSE // DACL defaulted
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ UserRightGenericMapping.GenericRead = PRIVILEGE_VIEW | STANDARD_RIGHTS_READ;
+ UserRightGenericMapping.GenericWrite = PRIVILEGE_ADJUST | STANDARD_RIGHTS_WRITE;
+ UserRightGenericMapping.GenericExecute = STANDARD_RIGHTS_EXECUTE;
+ UserRightGenericMapping.GenericAll = PRIVILEGE_ALL;
+
+ //
+ // Now open the Lsa process's token with appropriate access (token is
+ // needed to create the security object).
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY,
+ &LsaProcessTokenHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ Status = RtlNewSecurityObject(
+ NULL,
+ &AbsoluteDescriptor,
+ &UserRightSD,
+ FALSE, // not directory object
+ LsaProcessTokenHandle,
+ &UserRightGenericMapping
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ RtlInitUnicodeString(
+ &UserRightTypeName,
+ L"UserRightObject"
+ );
+
+Cleanup:
+ if (Dacl != NULL) {
+ LsapFreeLsaHeap(Dacl);
+ }
+ if (LsaProcessTokenHandle != NULL) {
+ NtClose(LsaProcessTokenHandle);
+ }
+
+ return(Status);
+
+}
+
+
+
+
+NTSTATUS
+LsapDbFindNextSidWithRight(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ IN OPTIONAL PLUID Privilege,
+ IN OPTIONAL PULONG SystemAccess,
+ OUT PLSAPR_SID *NextSid
+ )
+
+/*++
+
+Routine Description:
+
+ This function finds the next Sid of object of a given type within a
+ container object. The given object type must be one where objects
+ have Sids. The Sids returned can be used on subsequent open calls to
+ access the objects. The Account
+
+Arguments:
+
+ ContainerHandle - Handle to container object.
+
+ EnumerationContext - Pointer to a variable containing the index of
+ the object to be found. A zero value indicates that the first
+ object is to be found.
+
+ Privilege - If present, determines what privilge the account must have.
+
+ SystemAccess - If present, determines what kind of system access the
+ account must have.
+
+ NextSid - Receives a pointer to the next Sid found.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - Invalid ContainerHandle specified
+
+ STATUS_NO_MORE_ENTRIES - Warning that no more entries exist.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ ULONG SidKeyValueLength = 0;
+ ULONG RightKeyValueLength = 0;
+ UNICODE_STRING SubKeyNameU;
+ UNICODE_STRING SidKeyNameU;
+ UNICODE_STRING RightKeyNameU;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ContDirKeyHandle = NULL;
+ HANDLE SidKeyHandle = NULL;
+ HANDLE RightKeyHandle = NULL;
+ PSID ObjectSid = NULL;
+ PPRIVILEGE_SET ObjectPrivileges = NULL;
+ PULONG ObjectAccess = NULL;
+ PBYTE ObjectRights = NULL;
+ ULONG Index;
+ BOOLEAN ValidSid = FALSE;
+
+ //
+ // Zero pointers for cleanup routine
+ //
+
+ SidKeyNameU.Buffer = NULL;
+ RightKeyNameU.Buffer = NULL;
+
+ //
+ // Setup object attributes for opening the appropriate Containing
+ // Directory. Since we're looking for Account objects,
+ // the containing Directory is "Accounts". The Unicode strings for
+ // containing Directories are set up during Lsa Initialization.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &LsapDbContDirs[AccountObject],
+ OBJ_CASE_INSENSITIVE,
+ ((LSAP_DB_HANDLE) ContainerHandle)->KeyHandle,
+ NULL
+ );
+
+ Status = RtlpNtOpenKey(
+ &ContDirKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ ContDirKeyHandle = NULL; // For error processing
+ goto FindNextError;
+ }
+
+ //
+ // Initialize the Unicode String in which the next object's Logical Name
+ // will be returned. The Logical Name of an object equals its Registry
+ // Key relative to its Containing Directory, and is also equal to
+ // the Relative Id of the object represented in character form as an
+ // 8-digit number with leading zeros.
+ //
+ // NOTE: The size of buffer allocated for the Logical Name must be
+ // calculated dynamically when the Registry supports long names, because
+ // it is possible that the Logical Name of an object will be equal to a
+ // character representation of the full Sid, not just the Relative Id
+ // part.
+ //
+
+ SubKeyNameU.MaximumLength = (USHORT) LSAP_DB_LOGICAL_NAME_MAX_LENGTH;
+ SubKeyNameU.Length = 0;
+ SubKeyNameU.Buffer = LsapAllocateLsaHeap(SubKeyNameU.MaximumLength);
+
+ if (SubKeyNameU.Buffer == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto FindNextError;
+ }
+
+ //
+ // Now enumerate the next subkey.
+ //
+
+ Status = RtlpNtEnumerateSubKey(
+ ContDirKeyHandle,
+ &SubKeyNameU,
+ *EnumerationContext,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ //
+ // If a right was passed in, check for that right
+ //
+
+ if ((Privilege != NULL) || (SystemAccess != NULL)){
+
+ ASSERT(((Privilege == NULL) && (SystemAccess != NULL)) ||
+ ((SystemAccess == NULL) && (Privilege != NULL)));
+
+ //
+ // Construct a path to the Privilgs attribute of the object relative to
+ // the containing directory. This path has the form
+ //
+ // <Object Logical Name>"\Privilgs"
+ //
+ // The Logical Name of the object has just been returned by the
+ // above call to RtlpNtEnumerateSubKey.
+ //
+
+ if (Privilege != NULL) {
+ Status = LsapDbJoinSubPaths(
+ &SubKeyNameU,
+ &LsapDbNames[Privilgs],
+ &RightKeyNameU
+ );
+ } else {
+ Status = LsapDbJoinSubPaths(
+ &SubKeyNameU,
+ &LsapDbNames[ActSysAc],
+ &RightKeyNameU
+ );
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ //
+ // Setup object attributes for opening the privilege or access attribute
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &RightKeyNameU,
+ OBJ_CASE_INSENSITIVE,
+ ContDirKeyHandle,
+ NULL
+ );
+
+ //
+ // Open the Sid attribute
+ //
+
+ Status = RtlpNtOpenKey(
+ &RightKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = STATUS_NOT_ALL_ASSIGNED;
+ (*EnumerationContext)++;
+ }
+
+ SidKeyHandle = NULL;
+ goto FindNextError;
+ }
+
+ //
+ // Now query the size of the buffer required to read the Sid
+ // attribute's value.
+ //
+
+ RightKeyValueLength = 0;
+
+ Status = RtlpNtQueryValueKey(
+ RightKeyHandle,
+ NULL,
+ NULL,
+ &RightKeyValueLength,
+ NULL
+ );
+
+ //
+ // We expect buffer overflow to be returned from a query buffer size
+ // call.
+ //
+
+ if (Status == STATUS_BUFFER_OVERFLOW) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ goto FindNextError;
+ }
+
+ //
+ // Allocate memory for reading the Privileges attribute.
+ //
+
+ ObjectRights = MIDL_user_allocate(RightKeyValueLength);
+
+ if (ObjectRights == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto FindNextError;
+ }
+
+ //
+ // Supplied buffer is large enough to hold the SubKey's value.
+ // Query the value.
+ //
+
+ Status = RtlpNtQueryValueKey(
+ RightKeyHandle,
+ NULL,
+ ObjectRights,
+ &RightKeyValueLength,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ //
+ // Check for the system access or privilege specified
+ //
+
+ if (Privilege != NULL) {
+
+ ObjectPrivileges = (PPRIVILEGE_SET) ObjectRights;
+
+ for (Index = 0; Index < ObjectPrivileges->PrivilegeCount ; Index++) {
+ if (RtlEqualLuid(&ObjectPrivileges->Privilege[Index].Luid, Privilege)) {
+ ValidSid = TRUE;
+ break;
+ }
+ }
+ } else if (SystemAccess != NULL) {
+ ObjectAccess = (PULONG) ObjectRights;
+
+ if (((*ObjectAccess) & (*SystemAccess)) != 0) {
+ ValidSid = TRUE;
+ }
+ }
+
+ //
+ // If this sid didn't meet the criteria, return now. Make sure
+ // to bump up the context so we don't try this sid again.
+ //
+
+ if (!ValidSid) {
+ Status = STATUS_NOT_ALL_ASSIGNED;
+ (*EnumerationContext)++;
+ goto FindNextFinish;
+ }
+
+ } // privilege != NULL || systemaccess != NULL
+
+ //
+ // Construct a path to the Sid attribute of the object relative to
+ // the containing directory. This path has the form
+ //
+ // <Object Logical Name>"\Sid"
+ //
+ // The Logical Name of the object has just been returned by the
+ // above call to RtlpNtEnumerateSubKey.
+ //
+
+ Status = LsapDbJoinSubPaths(
+ &SubKeyNameU,
+ &LsapDbNames[Sid],
+ &SidKeyNameU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ //
+ // Setup object attributes for opening the Sid attribute
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &SidKeyNameU,
+ OBJ_CASE_INSENSITIVE,
+ ContDirKeyHandle,
+ NULL
+ );
+
+ //
+ // Open the Sid attribute
+ //
+
+ Status = RtlpNtOpenKey(
+ &SidKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ SidKeyHandle = NULL;
+ goto FindNextError;
+ }
+
+ //
+ // Now query the size of the buffer required to read the Sid
+ // attribute's value.
+ //
+
+ SidKeyValueLength = 0;
+
+ Status = RtlpNtQueryValueKey(
+ SidKeyHandle,
+ NULL,
+ NULL,
+ &SidKeyValueLength,
+ NULL
+ );
+
+ //
+ // We expect buffer overflow to be returned from a query buffer size
+ // call.
+ //
+
+ if (Status == STATUS_BUFFER_OVERFLOW) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ goto FindNextError;
+ }
+
+ //
+ // Allocate memory for reading the Sid attribute.
+ //
+
+ ObjectSid = MIDL_user_allocate(SidKeyValueLength);
+
+ if (ObjectSid == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto FindNextError;
+ }
+
+ //
+ // Supplied buffer is large enough to hold the SubKey's value.
+ // Query the value.
+ //
+
+ Status = RtlpNtQueryValueKey(
+ SidKeyHandle,
+ NULL,
+ ObjectSid,
+ &SidKeyValueLength,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto FindNextError;
+ }
+
+ (*EnumerationContext)++;
+
+ //
+ // Return the Sid.
+ //
+
+ *NextSid = ObjectSid;
+
+FindNextFinish:
+
+ //
+ // Cleanup the rights check
+ //
+
+ if (RightKeyHandle != NULL) {
+
+ SecondaryStatus = NtClose(RightKeyHandle);
+
+#if DBG
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ DbgPrint("LsapDbFindNextSid: NtClose failed 0x%lx\n", Status);
+ }
+
+#endif // DBG
+
+
+ }
+
+ if (ObjectRights != NULL) {
+ MIDL_user_free(ObjectRights);
+ }
+
+ //
+ // If necessary, close the Sid key handle
+ //
+
+ if (SidKeyHandle != NULL) {
+
+ SecondaryStatus = NtClose(SidKeyHandle);
+
+#if DBG
+
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ DbgPrint("LsapDbFindNextSid: NtClose failed 0x%lx\n", Status);
+ }
+
+#endif // DBG
+
+ }
+
+ //
+ // If necessary, close the containing directory handle
+ //
+
+ if (ContDirKeyHandle != NULL) {
+
+ SecondaryStatus = NtClose(ContDirKeyHandle);
+
+#if DBG
+ if (!NT_SUCCESS(SecondaryStatus)) {
+
+ DbgPrint(
+ "LsapDbFindNextSid: NtClose failed 0x%lx\n",
+ Status
+ );
+ }
+
+#endif // DBG
+
+ }
+
+ //
+ // If necessary, free the Unicode String buffer allocated by
+ // LsapDbJoinSubPaths for the Registry key name of the Sid attribute
+ // relative to the containing directory Registry key.
+ //
+
+ if (SidKeyNameU.Buffer != NULL) {
+
+ RtlFreeUnicodeString( &SidKeyNameU );
+ }
+
+ //
+ // If necessary, free the Unicode String buffer allocated for
+ // Registry key name of the object relative to its containing
+ // directory.
+ //
+
+ if (SubKeyNameU.Buffer != NULL) {
+
+ LsapFreeLsaHeap( SubKeyNameU.Buffer );
+ }
+
+ return(Status);
+
+FindNextError:
+
+ //
+ // If necessary, free the memory allocated for the object's Sid.
+ //
+
+ if (ObjectSid != NULL) {
+
+ MIDL_user_free(ObjectSid);
+ *NextSid = NULL;
+ }
+
+ goto FindNextFinish;
+}
+
+
+
+NTSTATUS
+LsapDbEnumerateSidsWithRight(
+ IN LSAPR_HANDLE ContainerHandle,
+ IN OPTIONAL PLUID Privilege,
+ IN OPTIONAL PULONG SystemAccess,
+ OUT PLSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function enumerates Sids of objects of a given type within a container
+ object. Since there may be more information than can be returned in a
+ single call of the routine, multiple calls can be made to get all of the
+ information. To support this feature, the caller is provided with a
+ handle that can be used across calls. On the initial call,
+ EnumerationContext should point to a variable that has been initialized
+ to 0.
+
+Arguments:
+
+ ContainerHandle - Handle to a container object.
+
+ Privilege - If present, specifies what privilege the account must have.
+
+ SystemAccess - If present, specifies what access type the account must
+ have. This cannot be present with Privilege.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ DbEnumerationBuffer - Receives a pointer to a structure that will receive
+ the count of entries returned in an enumeration information array, and
+ a pointer to the array. Currently, the only information returned is
+ the object Sids. These Sids may be used together with object type to
+ open the objects and obtain any further information available.
+
+
+ CountReturned - Pointer to variable which will receive a count of the
+ entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if no objects have been enumerated because the
+ EnumerationContext value passed in is too high.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSAP_DB_ENUMERATION_ELEMENT LastElement;
+ PLSAP_DB_ENUMERATION_ELEMENT FirstElement, NextElement = NULL, FreeElement;
+ PSID *Sids = NULL;
+ BOOLEAN PreferedMaximumReached = FALSE;
+ ULONG EntriesRead;
+ ULONG Index, EnumerationIndex;
+ BOOLEAN TrustedClient = ((LSAP_DB_HANDLE) ContainerHandle)->Trusted;
+
+ LastElement.Next = NULL;
+ FirstElement = &LastElement;
+
+ //
+ // If no enumeration buffer provided, return an error.
+ //
+
+
+ if ( !ARGUMENT_PRESENT(DbEnumerationBuffer) ) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+ //
+ // Enumerate objects, stopping when the length of data to be returned
+ // reaches or exceeds the Prefered Maximum Length, or reaches the
+ // absolute maximum allowed for LSA object enumerations. We allow
+ // the last object enumerated to bring the total amount of data to
+ // be returned beyond the Prefered Maximum Length, but not beyond the
+ // absolute maximum length.
+ //
+
+ EnumerationIndex = 0;
+
+ for (EntriesRead = 0;;) {
+
+ //
+ // Allocate memory for next enumeration element (if we haven't
+ // already). Set the Sid field to NULL for cleanup purposes.
+ //
+
+ if (NextElement == NULL ) {
+ NextElement = MIDL_user_allocate(sizeof (LSAP_DB_ENUMERATION_ELEMENT));
+
+ if (NextElement == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ }
+
+ NextElement->Sid = NULL;
+
+ //
+ // Find the next object's Sid, and fill in its object information.
+ // Note that memory will be allocated via MIDL_user_allocate
+ // and must be freed when no longer required.
+ //
+
+ Status = LsapDbFindNextSidWithRight(
+ ContainerHandle,
+ &EnumerationIndex,
+ Privilege,
+ SystemAccess,
+ (PLSAPR_SID *) &NextElement->Sid
+ );
+
+ //
+ // Stop the enumeration if any error or warning occurs. Note
+ // that the warning STATUS_NO_MORE_ENTRIES will be returned when
+ // we've gone beyond the last index.
+ //
+
+ if (Status != STATUS_SUCCESS) {
+
+ //
+ // If it failed because it was missing the privilege, continue
+ //
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+ continue;
+ }
+
+ //
+ // Since NextElement is not on the list, it will not get
+ // freed at the end so we must free it here.
+ //
+
+ MIDL_user_free( NextElement );
+ break;
+ }
+
+
+ //
+ // Link the object just found to the front of the enumeration list
+ //
+
+ NextElement->Next = FirstElement;
+ FirstElement = NextElement;
+ NextElement = NULL;
+ EntriesRead++;
+ }
+
+ //
+ // If an error other than STATUS_NO_MORE_ENTRIES occurred, return it.
+ // If STATUS_NO_MORE_ENTRIES was returned, we have enumerated all of the
+ // entries. In this case, return STATUS_SUCCESS if we enumerated at
+ // least one entry, otherwise propagate STATUS_NO_MORE_ENTRIES back to
+ // the caller.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+
+ goto EnumerateSidsError;
+ }
+
+ if (EntriesRead == 0) {
+
+ goto EnumerateSidsError;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Some entries were read, allocate an information buffer for returning
+ // them.
+ //
+
+ Sids = (PSID *) MIDL_user_allocate( sizeof (PSID) * EntriesRead );
+
+ if (Sids == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto EnumerateSidsError;
+ }
+
+ //
+ // Memory was successfully allocated for the return buffer.
+ // Copy in the enumerated Sids.
+ //
+
+ for (NextElement = FirstElement, Index = 0;
+ NextElement != &LastElement;
+ NextElement = NextElement->Next, Index++) {
+
+ ASSERT(Index < EntriesRead);
+
+ Sids[Index] = NextElement->Sid;
+ }
+
+EnumerateSidsFinish:
+
+ //
+ // Free the enumeration element structures (if any).
+ //
+
+ for (NextElement = FirstElement; NextElement != &LastElement;) {
+
+ //
+ // If an error has occurred, dispose of memory allocated
+ // for any Sids.
+ //
+
+ if (!(NT_SUCCESS(Status) || (Status == STATUS_NO_MORE_ENTRIES))) {
+
+ if (NextElement->Sid != NULL) {
+
+ MIDL_user_free(NextElement->Sid);
+ }
+ }
+
+ //
+ // Free the memory allocated for the enumeration element.
+ //
+
+ FreeElement = NextElement;
+ NextElement = NextElement->Next;
+
+ MIDL_user_free(FreeElement);
+ }
+
+ //
+ // Fill in return enumeration structure (0 and NULL in error case).
+ //
+
+ DbEnumerationBuffer->EntriesRead = EntriesRead;
+ DbEnumerationBuffer->Sids = Sids;
+
+ return(Status);
+
+EnumerateSidsError:
+
+ //
+ // If necessary, free memory allocated for returning the Sids.
+ //
+
+ if (Sids != NULL) {
+
+ MIDL_user_free( Sids );
+ Sids = NULL;
+ }
+
+ goto EnumerateSidsFinish;
+}
+
+
+NTSTATUS
+LsarEnumerateAccountsWithUserRight(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN OPTIONAL PLSAPR_UNICODE_STRING UserRight,
+ OUT PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaEnumerateAccountsWithUserRight API.
+
+ The LsaEnumerateAccounts API returns information about the accounts
+ in the target system's Lsa Database. This call requires
+ LSA_ENUMERATE_ACCOUNTS access to the Policy object. Since this call
+ accesses the privileges of an account, you must have PRIVILEGE_VIEW
+ access to the pseudo-privilege object.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ UserRight - Name of the right that the account must have.
+
+ EnumerationBuffer - Pointer to an enumeration structure that will receive
+ a count of the accounts enumerated on this call and a pointer to
+ an array of entries containing information for each enumerated
+ account.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if no objects are enumerated because the
+ EnumerationContext value passed in is too high.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAP_DB_SID_ENUMERATION_BUFFER DbEnumerationBuffer;
+ ULONG MaxLength;
+ ULONG Index;
+ ULONG SystemAccess = 0;
+ LUID PrivilegeValue;
+ PLUID Privilege = NULL;
+ PULONG Access = NULL;
+ ACCESS_MASK GrantedAccess;
+ NTSTATUS AccessStatus;
+ BOOLEAN GenerateOnClose;
+
+
+ //
+ // If no Enumeration Structure or index is provided, return an error.
+ //
+
+ if ( !ARGUMENT_PRESENT(EnumerationBuffer) ) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Initialize the internal Lsa Database Enumeration Buffer, and
+ // the provided Enumeration Buffer to NULL.
+ //
+
+ DbEnumerationBuffer.EntriesRead = 0;
+ DbEnumerationBuffer.Sids = NULL;
+ EnumerationBuffer->EntriesRead = 0;
+ EnumerationBuffer->Information = NULL;
+
+ //
+ // Acquire the Lsa Database lock. Verify that the connection handle is
+ // valid, is of the expected type and has all of the desired accesses
+ // granted. Reference the handle.
+ //
+
+ Status = LsapDbReferenceObject(
+ PolicyHandle,
+ 0,
+ PolicyObject,
+ LSAP_DB_ACQUIRE_LOCK
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // Impersonate the caller
+ //
+
+ Status = I_RpcMapWin32Status(RpcImpersonateClient(0));
+ if (!NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // Do an access check on with the UserRight security descriptor.
+ //
+
+ Status = NtAccessCheckAndAuditAlarm(
+ &LsapState.SubsystemName,
+ PolicyHandle,
+ &UserRightTypeName,
+ &UserRightTypeName,
+ UserRightSD,
+ PRIVILEGE_VIEW,
+ &UserRightGenericMapping,
+ FALSE,
+ &GrantedAccess,
+ &AccessStatus,
+ &GenerateOnClose
+ );
+
+ (VOID) RpcRevertToSelf();
+
+ //
+ // Check both error codes
+ //
+
+ if (!NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ Status = AccessStatus;
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // If a right was specified, translate it to a privilege or a
+ // system access type.
+ //
+
+
+ if (UserRight != NULL) {
+
+ //
+ // Convert the user right string into a privilege or a system
+ // access flag.
+ //
+
+ for (Index = 0; Index < LSAP_DB_SYSTEM_ACCESS_TYPES; Index++ ) {
+
+ if (RtlCompareUnicodeString(
+ &LsapDbRightAndAccess[Index].UserRight,
+ (PUNICODE_STRING) UserRight,
+ TRUE ) == 0) { // case insensitive
+
+ SystemAccess = LsapDbRightAndAccess[Index].SystemAccess;
+ Access = &SystemAccess;
+ break;
+ }
+ }
+
+ //
+ // Of system access is zero, try looking up the privilege name.
+ //
+
+ if (Access == NULL) {
+ Status = LsarLookupPrivilegeValue(
+ PolicyHandle,
+ (PLSAPR_UNICODE_STRING) UserRight,
+ &PrivilegeValue
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ Privilege = &PrivilegeValue;
+ }
+
+
+ }
+
+ //
+ // Call general Sid enumeration routine.
+ //
+
+ Status = LsapDbEnumerateSidsWithRight(
+ PolicyHandle,
+ Privilege,
+ Access,
+ &DbEnumerationBuffer
+ );
+
+ //
+ // Copy the enumerated information to the output. We can use the
+ // information actually returned by LsapDbEnumerateSids because it
+ // happens to be in exactly the correct form.
+ //
+
+ EnumerationBuffer->EntriesRead = DbEnumerationBuffer.EntriesRead;
+ EnumerationBuffer->Information =
+ (PLSAPR_ACCOUNT_INFORMATION) DbEnumerationBuffer.Sids;
+
+
+Cleanup:
+
+ Status = LsapDbDereferenceObject(
+ &PolicyHandle,
+ PolicyObject,
+ LSAP_DB_RELEASE_LOCK,
+ (SECURITY_DB_DELTA_TYPE) 0,
+ Status
+ );
+
+
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsarEnumerateAccountRights(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID AccountSid,
+ OUT PLSAPR_USER_RIGHT_SET UserRights
+ )
+
+/*++
+
+Routine Description:
+
+ Returns all the rights of an account. This is done by gathering the
+ privileges and system access of an account and translating that into
+ an array of strings.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall. This API requires
+ no special access.
+
+ AccountSid - Sid of account to open.
+
+ UserRights - receives an array of user rights for the account
+
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - the caller did not have sufficient access to
+ return the privileges or system access of the account.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - the specified account did not exist.
+
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the
+ request.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAPR_HANDLE AccountHandle = NULL;
+ PLSAPR_PRIVILEGE_SET PrivilegeSet = NULL;
+
+ ULONG SystemAccess;
+ ULONG UserRightCount;
+ ULONG UserRightIndex;
+ ULONG PrivilegeIndex;
+ PUNICODE_STRING UserRightArray;
+ PUNICODE_STRING TempString;
+
+ //
+ // Open the account for ACCOUNT_VIEW access
+ //
+
+ Status = LsarOpenAccount(
+ PolicyHandle,
+ AccountSid,
+ ACCOUNT_VIEW,
+ &AccountHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // Get the system access flags
+ //
+
+ Status = LsarGetSystemAccessAccount(
+ AccountHandle,
+ &SystemAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Get the privilege information
+ //
+
+
+ Status = LsarEnumeratePrivilegesAccount(
+ AccountHandle,
+ &PrivilegeSet
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Repackage the privileges and system access as user rights
+ //
+
+ UserRightCount = 0;
+
+ for (PrivilegeIndex = 0;
+ PrivilegeIndex < LSAP_DB_SYSTEM_ACCESS_TYPES;
+ PrivilegeIndex++ ) {
+
+ if ((SystemAccess & LsapDbRightAndAccess[PrivilegeIndex].SystemAccess) != 0 ) {
+ UserRightCount++;
+ }
+ }
+
+ UserRightCount += PrivilegeSet->PrivilegeCount;
+
+ //
+ // If there were no rights, say that and cleanup.
+ //
+
+ if (UserRightCount == 0) {
+
+ UserRights->Entries = 0;
+ UserRights->UserRights = NULL;
+ Status = STATUS_SUCCESS;
+ goto Cleanup;
+ }
+
+ UserRightArray = (PUNICODE_STRING)
+ MIDL_user_allocate(UserRightCount * sizeof(LSAPR_UNICODE_STRING));
+
+ if (UserRightArray == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ //
+ // Zero this in case we have to clean it up partially.
+ //
+
+ RtlZeroMemory(
+ UserRightArray,
+ UserRightCount * sizeof(LSAPR_UNICODE_STRING)
+ );
+
+ UserRightIndex = 0;
+ for (PrivilegeIndex = 0;
+ PrivilegeIndex < PrivilegeSet->PrivilegeCount ;
+ PrivilegeIndex++ ) {
+
+ TempString = NULL;
+ Status = LsarLookupPrivilegeName(
+ PolicyHandle,
+ (PLUID) &PrivilegeSet->Privilege[PrivilegeIndex].Luid,
+ (PLSAPR_UNICODE_STRING *) &TempString
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // The name that came back was allocated in two parts, the buffer
+ // and the string. Copy the buffer pointer to the return array
+ // and free the string structure.
+ //
+
+ UserRightArray[UserRightIndex++] = * TempString;
+ MIDL_user_free(TempString);
+
+
+ }
+
+ //
+ // Copy in the system access rights
+ for (PrivilegeIndex = 0;
+ PrivilegeIndex < LSAP_DB_SYSTEM_ACCESS_TYPES;
+ PrivilegeIndex++ ) {
+
+ if ((SystemAccess & LsapDbRightAndAccess[PrivilegeIndex].SystemAccess) != 0 ) {
+
+ //
+ // Allocate a new string and copy the access name into it.
+ //
+
+ UserRightArray[UserRightIndex] = LsapDbRightAndAccess[PrivilegeIndex].UserRight;
+ UserRightArray[UserRightIndex].Buffer = (LPWSTR)
+ MIDL_user_allocate(LsapDbRightAndAccess[PrivilegeIndex].UserRight.MaximumLength);
+
+ if (UserRightArray[UserRightIndex].Buffer == NULL ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ RtlCopyUnicodeString(
+ (PUNICODE_STRING) &UserRightArray[UserRightIndex],
+ &LsapDbRightAndAccess[PrivilegeIndex].UserRight
+ );
+
+ UserRightIndex++;
+ }
+ }
+ ASSERT(UserRightCount == UserRightIndex);
+
+ UserRights->Entries = UserRightCount;
+ UserRights->UserRights = (PLSAPR_UNICODE_STRING) UserRightArray;
+
+ Status = STATUS_SUCCESS;
+
+
+Cleanup:
+
+ //
+ // Cleanup the system rights if we failed
+ //
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (UserRightArray != NULL) {
+
+ for (UserRightIndex = 0;
+ UserRightIndex < UserRightCount ;
+ UserRightIndex++) {
+
+ if (UserRightArray[UserRightIndex].Buffer != NULL) {
+
+ MIDL_user_free(UserRightArray[UserRightIndex].Buffer);
+ }
+ }
+ }
+ }
+
+ if (AccountHandle != NULL) {
+ LsarClose(&AccountHandle);
+ }
+ if (PrivilegeSet != NULL) {
+ MIDL_user_free(PrivilegeSet);
+ }
+
+ return(Status);
+}
+
+NTSTATUS
+LsapDbConvertRightsToPrivileges(
+ IN PLSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_USER_RIGHT_SET UserRights,
+ OUT PLSAPR_PRIVILEGE_SET * PrivilegeSet,
+ OUT PULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ Converts an array of user right strings into a privilege set and a
+ system access flag.
+
+Arguments:
+
+ UserRights - Contains an array of strings and a count of those strings.
+
+ PrivilegeSet - receives a privilege set of those rights corresponding
+ to privilges, allocated with MIDL_user_allocate.
+
+ SystemAccess - receives the access flags specified by the user rights
+
+Return Value:
+
+ STATUS_NO_SUCH_PRIVILEGE - the user right could not be converted into
+ a privilege or an access type.
+
+ STATUS_INSUFFICIENT_RESOURCES - there was not enough memory to translate
+ the rights to privileges.
+
+--*/
+
+{
+ ULONG PrivilegeCount;
+ PLSAPR_PRIVILEGE_SET Privileges = NULL;
+ ULONG Access = 0;
+ ULONG PrivilegeSetSize;
+ ULONG PrivilegeIndex = 0;
+ ULONG RightIndex;
+ ULONG AccessIndex;
+ NTSTATUS Status;
+
+ PrivilegeSetSize = sizeof(LSAPR_PRIVILEGE_SET) +
+ (UserRights->Entries-1) * sizeof(LUID_AND_ATTRIBUTES);
+
+
+ Privileges = (PLSAPR_PRIVILEGE_SET) MIDL_user_allocate(PrivilegeSetSize);
+ if (Privileges == NULL) {
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ for (RightIndex = 0;
+ RightIndex < UserRights->Entries;
+ RightIndex++ ) {
+
+ //
+ // First try to map the right as a privilege
+ //
+
+ if (NT_SUCCESS(LsarLookupPrivilegeValue(
+ PolicyHandle,
+ (PLSAPR_UNICODE_STRING) &UserRights->UserRights[RightIndex],
+ (PLUID) &Privileges->Privilege[PrivilegeIndex].Luid))) {
+ Privileges->Privilege[PrivilegeIndex].Attributes = 0;
+ PrivilegeIndex++;
+ } else {
+
+ //
+ // Try to map it to a system access type
+ //
+
+ for (AccessIndex = 0;
+ AccessIndex < LSAP_DB_SYSTEM_ACCESS_TYPES;
+ AccessIndex++ ) {
+
+ if (RtlCompareUnicodeString(
+ &LsapDbRightAndAccess[AccessIndex].UserRight,
+ (PUNICODE_STRING) &UserRights->UserRights[RightIndex],
+ FALSE) == 0) { // case sensistive
+
+ Access |= LsapDbRightAndAccess[AccessIndex].SystemAccess;
+ break;
+ }
+ }
+ if (AccessIndex == LSAP_DB_SYSTEM_ACCESS_TYPES) {
+ Status = STATUS_NO_SUCH_PRIVILEGE;
+ goto Cleanup;
+ }
+
+ }
+ }
+ Privileges->PrivilegeCount = PrivilegeIndex;
+ *PrivilegeSet = Privileges;
+ *SystemAccess = Access;
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ if (!NT_SUCCESS(Status)) {
+ if (Privileges != NULL) {
+ MIDL_user_free(Privileges);
+ }
+ }
+ return(Status);
+
+}
+
+NTSTATUS
+LsarAddAccountRights(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID AccountSid,
+ IN PLSAPR_USER_RIGHT_SET UserRights
+ )
+/*++
+
+Routine Description:
+
+ Adds rights to the account specified by the account sid. If the account
+ does not exist, it creates the account.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call. The handle must have
+ POLICY_CREATE_ACCOUNT access if this is the first call for this
+ AccountSid.
+
+ AccountSid - Sid of account to add rights to
+
+ UserRights - Array of unicode strings naming rights to add to the
+ account.
+
+Return Value:
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
+
+ STATUS_INVALID_PARAMTER - one of the parameters was not present
+
+ STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
+
+ STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
+ account to add privileges.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAPR_HANDLE AccountHandle = NULL;
+ PLSAPR_PRIVILEGE_SET PrivilegeSet = NULL;
+ ULONG SystemAccess;
+ ULONG OldSystemAccess;
+ BOOLEAN ChangedAccess = FALSE;
+
+ //
+ // Make sure we have all the arguments we need
+ //
+
+ if (!ARGUMENT_PRESENT(UserRights)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if (!ARGUMENT_PRESENT(AccountSid)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Open the account for ACCOUNT_VIEW access
+ //
+
+ Status = LsarOpenAccount(
+ PolicyHandle,
+ AccountSid,
+ ACCOUNT_ADJUST_PRIVILEGES | ACCOUNT_ADJUST_SYSTEM_ACCESS | ACCOUNT_VIEW,
+ &AccountHandle
+ );
+ //
+ // If the account did not exist, try to create it.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = LsarCreateAccount(
+ PolicyHandle,
+ AccountSid,
+ ACCOUNT_ADJUST_PRIVILEGES | ACCOUNT_ADJUST_SYSTEM_ACCESS | ACCOUNT_VIEW,
+ &AccountHandle
+ );
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ return(Status);
+ }
+
+ Status = LsapDbConvertRightsToPrivileges(
+ PolicyHandle,
+ UserRights,
+ &PrivilegeSet,
+ &SystemAccess
+ );
+ if (!NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // If system access was changed, add it
+ //
+
+ if (SystemAccess != 0) {
+ Status = LsarGetSystemAccessAccount(
+ AccountHandle,
+ &OldSystemAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ SystemAccess = SystemAccess | OldSystemAccess;
+
+ Status = LsarSetSystemAccessAccount(
+ AccountHandle,
+ SystemAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ ChangedAccess = TRUE;
+ }
+ //
+ // If privileges were changed, add them.
+ //
+
+ if (PrivilegeSet->PrivilegeCount != 0) {
+
+ Status = LsarAddPrivilegesToAccount(
+ AccountHandle,
+ PrivilegeSet
+ );
+
+ }
+
+Cleanup:
+ //
+ // If we didn't make both changes, unroll the one we did
+ //
+
+
+ if (!NT_SUCCESS(Status) && ChangedAccess) {
+
+ //
+ // Ignore the error code since this is a last-ditch effort
+ //
+
+ (void) LsarSetSystemAccessAccount(
+ AccountHandle,
+ OldSystemAccess
+ );
+
+ }
+
+ if (PrivilegeSet != NULL) {
+ MIDL_user_free(PrivilegeSet);
+ }
+
+ if (AccountHandle != NULL) {
+ LsarClose(&AccountHandle);
+ }
+
+ return(Status);
+
+}
+
+NTSTATUS
+LsarRemoveAccountRights(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID AccountSid,
+ IN BOOLEAN AllRights,
+ IN PLSAPR_USER_RIGHT_SET UserRights
+ )
+/*++
+
+Routine Description:
+
+ Removes rights to the account specified by the account sid. If the
+ AllRights flag is set or if all the rights are removed, the account
+ is deleted.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call
+
+ AccountSid - Sid of account to remove rights from
+
+ UserRights - Array of unicode strings naming rights to remove from the
+ account.
+
+Return Value:
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
+
+ STATUS_INVALID_PARAMTER - one of the parameters was not present
+
+ STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
+
+ STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
+ account to add privileges.
+
+--*/
+{
+ NTSTATUS Status;
+ LSAPR_HANDLE AccountHandle = NULL;
+ PLSAPR_PRIVILEGE_SET PrivilegeSet = NULL;
+ ULONG SystemAccess;
+ ULONG OldSystemAccess;
+ BOOLEAN ChangedAccess = FALSE;
+ PLSAPR_PRIVILEGE_SET FinalPrivilegeSet = NULL;
+
+
+ //
+ // Open the account for ACCOUNT_VIEW access
+ //
+
+ Status = LsarOpenAccount(
+ PolicyHandle,
+ AccountSid,
+ ACCOUNT_ADJUST_PRIVILEGES |
+ ACCOUNT_ADJUST_SYSTEM_ACCESS |
+ ACCOUNT_VIEW |
+ DELETE,
+ &AccountHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+ //
+ // Convert the rights to privileges only if they don't want all
+ // rights removed. In that case, we don't care.
+ //
+
+ if (AllRights == FALSE) {
+ Status = LsapDbConvertRightsToPrivileges(
+ PolicyHandle,
+ UserRights,
+ &PrivilegeSet,
+ &SystemAccess
+ );
+ if (!NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+ } else {
+ Status = LsarDeleteObject(
+ &AccountHandle
+ );
+ //
+ // If the handle was deleted, zero it so we don't try to
+ // close it later.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ AccountHandle = NULL;
+ }
+ goto Cleanup;
+ }
+
+ //
+ // If system access was changed, add it
+ //
+
+ Status = LsarGetSystemAccessAccount(
+ AccountHandle,
+ &OldSystemAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // After this block of code, SystemAccess should contain the final
+ // access for the account.
+ //
+
+ if (SystemAccess != 0) {
+
+ SystemAccess = OldSystemAccess & ~SystemAccess;
+
+ Status = LsarSetSystemAccessAccount(
+ AccountHandle,
+ SystemAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ ChangedAccess = TRUE;
+ } else {
+
+ SystemAccess = OldSystemAccess;
+ }
+
+ //
+ // If privileges were changed, add them.
+ //
+
+ if (AllRights || PrivilegeSet->PrivilegeCount != 0) {
+
+ Status = LsarRemovePrivilegesFromAccount(
+ AccountHandle,
+ FALSE, // don't remove all rights
+ PrivilegeSet
+ );
+
+ }
+
+ //
+ // Check to see if all the privileges have been removed - if so,
+ // and system access is 0, delete the account.
+ //
+
+ Status = LsarEnumeratePrivilegesAccount(
+ AccountHandle,
+ &FinalPrivilegeSet
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ if ((FinalPrivilegeSet->PrivilegeCount == 0) &&
+ (SystemAccess == 0)) {
+
+ //
+ // The account has no privileges or system access - delete it.
+ //
+
+ Status = LsarDeleteObject(
+ &AccountHandle
+ );
+ //
+ // If the handle was deleted, zero it so we don't try to
+ // close it later.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ AccountHandle = NULL;
+ }
+
+ }
+
+
+
+Cleanup:
+ //
+ // If we didn't make both changes, unroll the one we did
+ //
+
+
+ if (!NT_SUCCESS(Status) && ChangedAccess) {
+
+ //
+ // Ignore the error code since this is a last-ditch effort
+ //
+
+ (void) LsarSetSystemAccessAccount(
+ AccountHandle,
+ OldSystemAccess
+ );
+
+ }
+
+ if (PrivilegeSet != NULL) {
+ MIDL_user_free(PrivilegeSet);
+ }
+
+ if (AccountHandle != NULL) {
+ LsarClose(&AccountHandle);
+ }
+
+ return(Status);
+
+}
+
+NTSTATUS
+LsarQueryTrustedDomainInfo(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID TrustedDomainSid,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_TRUSTED_DOMAIN_INFO * TrustedDomainInformation
+ )
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaQueryInfoTrustedDomain API.
+
+ The LsaQueryInfoTrustedDomain API obtains information from a
+ TrustedDomain object. The caller must have access appropriate to the
+ information being requested (see InformationClass parameter). It also
+ may query the secret object (for the TrustedDomainPasswordInformation
+ class).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to query.
+
+ InformationClass - Specifies the information to be returned.
+
+ Buffer - Receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+{
+
+ LSAPR_HANDLE DomainHandle = NULL;
+ LSAPR_HANDLE SecretHandle = NULL;
+ NTSTATUS Status;
+ PLSAPR_TRUSTED_DOMAIN_INFO DomainInfo = NULL;
+ PLSAPR_TRUSTED_PASSWORD_INFO PasswordInfo = NULL;
+ TRUSTED_INFORMATION_CLASS ClassToUse;
+ ULONG DesiredAccess;
+ BOOLEAN QueryPassword = FALSE;
+ UNICODE_STRING SecretName;
+ PLSAPR_CR_CIPHER_VALUE Password = NULL;
+ PLSAPR_CR_CIPHER_VALUE OldPassword = NULL;
+
+ SecretName.Buffer = NULL;
+
+ ClassToUse = InformationClass;
+ switch(InformationClass) {
+
+ case TrustedPasswordInformation:
+ QueryPassword = TRUE;
+ DesiredAccess = TRUSTED_QUERY_DOMAIN_NAME;
+ ClassToUse = TrustedDomainNameInformation;
+ break;
+
+ case TrustedDomainNameInformation:
+ DesiredAccess = TRUSTED_QUERY_DOMAIN_NAME;
+ break;
+
+ case TrustedControllersInformation:
+ //
+ // This info class is obsolete
+ //
+ return(STATUS_NOT_IMPLEMENTED);
+ break;
+ case TrustedPosixOffsetInformation:
+ DesiredAccess = TRUSTED_QUERY_POSIX;
+ break;
+
+ }
+
+ Status = LsarOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ DesiredAccess,
+ &DomainHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+
+ Status = LsarQueryInfoTrustedDomain(
+ DomainHandle,
+ ClassToUse,
+ &DomainInfo
+ );
+
+ //
+ // If the info we wanted was what we queried, cleanup now.
+ //
+
+ if (!QueryPassword) {
+ if (NT_SUCCESS(Status)) {
+ *TrustedDomainInformation = DomainInfo;
+ DomainInfo = NULL;
+ }
+ goto Cleanup;
+ }
+
+ //
+ // Build the secret name for the domain.
+ //
+
+
+ //
+ // Build the secret name
+ //
+
+ SecretName.Length = DomainInfo->TrustedDomainNameInfo.Name.Length;
+ SecretName.Length += (LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH) * sizeof(WCHAR);
+ SecretName.MaximumLength = SecretName.Length + sizeof(WCHAR);
+
+ SecretName.Buffer = (LPWSTR) MIDL_user_allocate(SecretName.MaximumLength);
+ if (SecretName.Buffer == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(
+ SecretName.Buffer,
+ LSA_GLOBAL_SECRET_PREFIX,
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
+ );
+ RtlCopyMemory(
+ SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH,
+ SSI_SECRET_PREFIX,
+ SSI_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
+ );
+ RtlCopyMemory(
+ SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH,
+ DomainInfo->TrustedDomainNameInfo.Name.Buffer,
+ DomainInfo->TrustedDomainNameInfo.Name.MaximumLength
+ );
+
+ //
+ // Free the domain info so we can re-use it lower down
+ //
+
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ TrustedDomainNameInformation,
+ DomainInfo
+ );
+ DomainInfo = NULL;
+
+ //
+ // Now try to open the secret
+ //
+
+ Status = LsarOpenSecret(
+ PolicyHandle,
+ (PLSAPR_UNICODE_STRING) &SecretName,
+ SECRET_QUERY_VALUE,
+ &SecretHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsarQuerySecret(
+ SecretHandle,
+ &Password,
+ NULL,
+ &OldPassword,
+ NULL
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Build a new domain info with the secret information
+ //
+
+ DomainInfo = (PLSAPR_TRUSTED_DOMAIN_INFO)
+ MIDL_user_allocate(sizeof(LSAPR_TRUSTED_DOMAIN_INFO));
+
+ if (DomainInfo == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ DomainInfo->TrustedPasswordInfo.Password = Password;
+ DomainInfo->TrustedPasswordInfo.OldPassword = OldPassword;
+
+ Password = NULL;
+
+ OldPassword = NULL;
+
+ *TrustedDomainInformation = DomainInfo;
+ DomainInfo = NULL;
+ Status = STATUS_SUCCESS;
+
+
+
+Cleanup:
+ if (SecretName.Buffer != NULL) {
+ MIDL_user_free(SecretName.Buffer);
+ }
+
+ if (DomainHandle != NULL) {
+ LsarClose(&DomainHandle);
+ }
+
+ if (SecretHandle != NULL) {
+ LsarClose(&SecretHandle);
+ }
+
+ if (DomainInfo != NULL) {
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ ClassToUse,
+ DomainInfo
+ );
+
+ }
+ if (Password != NULL) {
+ LsaIFree_LSAPR_CR_CIPHER_VALUE(Password);
+ }
+ if (OldPassword != NULL) {
+ LsaIFree_LSAPR_CR_CIPHER_VALUE(OldPassword);
+ }
+ return(Status);
+}
+
+NTSTATUS
+LsarSetTrustedDomainInfo(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID TrustedDomainSid,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the LSA server RPC worker routine for the
+ LsaSetInfoTrustedDomain API.
+
+ The LsaSetInformationTrustedDomain API modifies information in the Trusted
+ Domain Object and in the Secret Object. The caller must have access
+ appropriate to the information to be changed in the Policy Object, see
+ the InformationClass parameter.
+
+ If the domain does not yet exist and the information class is
+ TrustedDomainNameInformation, then the domain is created. If the
+ domain exists and the class is TrustedDomainNameInformation, an
+ error is returned.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to modify.
+
+ InformationClass - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ TrustedDomainNameInformation POLICY_TRUST_ADMIN
+ TrustedPosixOffsetInformation none
+ TrustedPasswordInformation POLICY_CREATE_SECRET
+
+ Buffer - Points to a structure containing the information appropriate
+ to the InformationClass parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+{
+ LSAPR_HANDLE DomainHandle = NULL;
+ LSAPR_HANDLE SecretHandle = NULL;
+ TRUSTED_INFORMATION_CLASS ClassToUse;
+ ULONG DesiredAccess = 0;
+ BOOLEAN SetPassword = FALSE;
+ LSAPR_TRUST_INFORMATION TrustInformation;
+ PLSAPR_TRUSTED_DOMAIN_INFO DomainInfo = NULL;
+ UNICODE_STRING SecretName;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ SecretName.Buffer = NULL;
+
+
+ //
+ // If the class is domain name, try to create the domain since you
+ // can't change the name of an existing domain.
+ //
+
+ if (InformationClass == TrustedDomainNameInformation) {
+
+ //
+ // Try to create the domain if we have the name information
+ //
+
+ TrustInformation.Name = TrustedDomainInformation->TrustedDomainNameInfo.Name;
+ TrustInformation.Sid = TrustedDomainSid;
+
+ Status = LsarCreateTrustedDomain(
+ PolicyHandle,
+ &TrustInformation,
+ 0, // desired access
+ &DomainHandle
+ );
+
+ goto Cleanup;
+ }
+
+ if (InformationClass == TrustedPosixOffsetInformation) {
+
+ //
+ // For posix information, we just do the normal set information
+ //
+
+ Status = LsarOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ TRUSTED_SET_POSIX,
+ &DomainHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ Status = LsarSetInformationTrustedDomain(
+ DomainHandle,
+ InformationClass,
+ TrustedDomainInformation
+ );
+ goto Cleanup;
+
+
+ }
+ if (InformationClass != TrustedPasswordInformation) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ //
+ // For posix information, we just do the normal set information
+ //
+
+ Status = LsarOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ TRUSTED_QUERY_DOMAIN_NAME,
+ &DomainHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Query the name of the domain so we know what secret to set
+ //
+
+ Status = LsarQueryInfoTrustedDomain(
+ DomainHandle,
+ TrustedDomainNameInformation,
+ &DomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+
+ //
+ // Build the secret name
+ //
+
+ SecretName.Length = DomainInfo->TrustedDomainNameInfo.Name.Length;
+ SecretName.Length += (LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH) * sizeof(WCHAR);
+ SecretName.MaximumLength = SecretName.Length + sizeof(WCHAR);
+
+ SecretName.Buffer = (LPWSTR) MIDL_user_allocate(SecretName.MaximumLength);
+ if (SecretName.Buffer == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(
+ SecretName.Buffer,
+ LSA_GLOBAL_SECRET_PREFIX,
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
+ );
+ RtlCopyMemory(
+ SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH,
+ SSI_SECRET_PREFIX,
+ SSI_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
+ );
+ RtlCopyMemory(
+ SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH,
+ DomainInfo->TrustedDomainNameInfo.Name.Buffer,
+ DomainInfo->TrustedDomainNameInfo.Name.MaximumLength
+ );
+
+ //
+ // Open the secret. If that fails, try to create it.
+ //
+
+ Status = LsarOpenSecret(
+ PolicyHandle,
+ (PLSAPR_UNICODE_STRING) &SecretName,
+ SECRET_SET_VALUE,
+ &SecretHandle
+ );
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = LsarCreateSecret(
+ PolicyHandle,
+ (PLSAPR_UNICODE_STRING) &SecretName,
+ SECRET_SET_VALUE,
+ &SecretHandle
+ );
+ }
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ Status = LsarSetSecret(
+ SecretHandle,
+ TrustedDomainInformation->TrustedPasswordInfo.Password,
+ TrustedDomainInformation->TrustedPasswordInfo.OldPassword
+ );
+
+
+
+
+Cleanup:
+ if (SecretName.Buffer != NULL) {
+ MIDL_user_free(SecretName.Buffer);
+ }
+
+ if (DomainInfo != NULL) {
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ TrustedDomainNameInformation,
+ DomainInfo
+ );
+ }
+ if (DomainHandle != NULL) {
+ LsarClose(&DomainHandle);
+ }
+ if (SecretHandle != NULL) {
+ LsarClose(&SecretHandle);
+ }
+
+ return(Status);
+}
+
+NTSTATUS
+LsarDeleteTrustedDomain(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_SID TrustedDomainSid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes a trusted domain and the associated secret.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to delete
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient access to delete
+ the requested domain.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - The requested domain does not exist.
+
+--*/
+
+{
+ LSAPR_HANDLE DomainHandle = NULL;
+ LSAPR_HANDLE SecretHandle = NULL;
+ UNICODE_STRING SecretName;
+ PLSAPR_TRUSTED_DOMAIN_INFO DomainInfo = NULL;
+ NTSTATUS Status;
+
+
+ SecretName.Buffer = NULL;
+
+ //
+ // Open the domain so we can find its name (to delete the secret)
+ // and then delete it.
+ //
+
+
+ Status = LsarOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ TRUSTED_QUERY_DOMAIN_NAME | DELETE,
+ &DomainHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Query the domain name so we can delete the secret
+ //
+
+ Status = LsarQueryInfoTrustedDomain(
+ DomainHandle,
+ TrustedDomainNameInformation,
+ &DomainInfo
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Build the secret name
+ //
+
+ SecretName.Length = DomainInfo->TrustedDomainNameInfo.Name.Length;
+ SecretName.Length += (LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH) * sizeof(WCHAR);
+ SecretName.MaximumLength = SecretName.Length + sizeof(WCHAR);
+
+ SecretName.Buffer = (LPWSTR) MIDL_user_allocate(SecretName.MaximumLength);
+ if (SecretName.Buffer == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(
+ SecretName.Buffer,
+ LSA_GLOBAL_SECRET_PREFIX,
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
+ );
+ RtlCopyMemory(
+ SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH,
+ SSI_SECRET_PREFIX,
+ SSI_SECRET_PREFIX_LENGTH * sizeof(WCHAR)
+ );
+ RtlCopyMemory(
+ SecretName.Buffer + LSA_GLOBAL_SECRET_PREFIX_LENGTH + SSI_SECRET_PREFIX_LENGTH,
+ DomainInfo->TrustedDomainNameInfo.Name.Buffer,
+ DomainInfo->TrustedDomainNameInfo.Name.MaximumLength
+ );
+
+ //
+ // Delete the domain
+ //
+
+ Status = LsarDeleteObject(&DomainHandle);
+ if (Status != STATUS_SUCCESS) {
+ goto Cleanup;
+ }
+ //
+ // Since we successfully deleted the secret, set it to zero so we don't
+ // try to close it later.
+ //
+
+ DomainHandle = NULL;
+
+ //
+ // Now try to open the secret and delete it.
+ //
+
+ Status = LsarOpenSecret(
+ PolicyHandle,
+ (PLSAPR_UNICODE_STRING) &SecretName,
+ DELETE,
+ &SecretHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If the secret does not exist, that just means that the password
+ // was never set.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = STATUS_SUCCESS;
+ }
+ goto Cleanup;
+ }
+
+ Status = LsarDeleteObject(&SecretHandle);
+
+ //
+ // If we successfully deleted the secret, set it to zero so we don't
+ // try to close it later.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ SecretHandle = NULL;
+ }
+
+Cleanup:
+ if (SecretHandle != NULL) {
+ LsarClose(&SecretHandle);
+ }
+
+ if (DomainHandle != NULL) {
+ LsarClose(&DomainHandle);
+ }
+
+ if (DomainInfo != NULL) {
+ LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
+ TrustedDomainNameInformation,
+ DomainInfo
+ );
+ }
+ if (SecretName.Buffer != NULL) {
+ MIDL_user_free(SecretName.Buffer);
+ }
+
+ return(Status);
+
+}
+
+NTSTATUS
+LsarStorePrivateData(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_UNICODE_STRING KeyName,
+ IN OPTIONAL PLSAPR_CR_CIPHER_VALUE EncryptedData
+ )
+/*++
+
+Routine Description:
+
+ This routine stores a secret under the name KeyName. If the password
+ is not present, the secret is deleted
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall. If this is the
+ first call, it requres POLICY_CREATE_SECRET access.
+
+ KeyName - Name to store the secret under.
+
+ EncryptedData - private data encrypted with session key.
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient privilege to set
+ the workstation password.
+
+--*/
+
+{
+ LSAPR_HANDLE SecretHandle = NULL;
+ NTSTATUS Status;
+ ULONG DesiredAccess;
+ BOOLEAN DeletePassword = FALSE;
+
+ if (ARGUMENT_PRESENT(EncryptedData)) {
+ DesiredAccess = SECRET_SET_VALUE;
+ } else {
+ DesiredAccess = DELETE;
+ DeletePassword = TRUE;
+ }
+
+
+ Status = LsarOpenSecret(
+ PolicyHandle,
+ KeyName,
+ DesiredAccess,
+ &SecretHandle
+ );
+
+ //
+ // If the secret didn't exist, and we aren't trying to delete it, create it.
+ //
+
+ if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) &&
+ (!DeletePassword)) {
+ Status = LsarCreateSecret(
+ PolicyHandle,
+ KeyName,
+ SECRET_SET_VALUE,
+ &SecretHandle
+ );
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ if (DeletePassword) {
+ Status = LsarDeleteObject(&SecretHandle);
+
+ //
+ // If we succeeded, zero the handle so we don't try to close
+ // it later.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ SecretHandle = NULL;
+ }
+
+ } else {
+
+ Status = LsarSetSecret(
+ SecretHandle,
+ EncryptedData,
+ EncryptedData
+ );
+
+ }
+
+Cleanup:
+ if (SecretHandle != NULL ) {
+ LsarClose(&SecretHandle);
+ }
+ return(Status);
+}
+
+NTSTATUS
+LsarRetrievePrivateData(
+ IN LSAPR_HANDLE PolicyHandle,
+ IN PLSAPR_UNICODE_STRING KeyName,
+ IN OUT PLSAPR_CR_CIPHER_VALUE *EncryptedData
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the workstation password stored in the
+ KeyName secret.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall
+
+ KeyName - Name of secret to retrieve
+
+ EncryptedData - Receives data encrypted with session key
+
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient access to get the
+ workstation password.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - there is no workstation password.
+
+--*/
+
+{
+ LSAPR_HANDLE SecretHandle = NULL;
+ NTSTATUS Status;
+
+
+ Status = LsarOpenSecret(
+ PolicyHandle,
+ KeyName,
+ SECRET_QUERY_VALUE,
+ &SecretHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsarQuerySecret(
+ SecretHandle,
+ EncryptedData,
+ NULL,
+ NULL,
+ NULL
+ );
+
+Cleanup:
+ if (SecretHandle != NULL ) {
+ LsarClose(&SecretHandle);
+ }
+ return(Status);
+}
diff --git a/private/lsa/server/main.c b/private/lsa/server/main.c
new file mode 100644
index 000000000..0cbe6d4b5
--- /dev/null
+++ b/private/lsa/server/main.c
@@ -0,0 +1,100 @@
+#include <windows.h>
+#include <stdio.h>
+
+int __cdecl main(int, char **, char **);
+
+typedef void (__cdecl *_PVFV)(void);
+
+#pragma data_seg(".CRT$XIA")
+_PVFV __xi_a[] = { NULL };
+
+
+#pragma data_seg(".CRT$XIZ")
+_PVFV __xi_z[] = { NULL };
+
+
+#pragma data_seg(".CRT$XCA")
+_PVFV __xc_a[] = { NULL };
+
+
+#pragma data_seg(".CRT$XCZ")
+_PVFV __xc_z[] = { NULL };
+
+
+#pragma data_seg(".CRT$XPA")
+_PVFV __xp_a[] = { NULL };
+
+
+#pragma data_seg(".CRT$XPZ")
+_PVFV __xp_z[] = { NULL };
+
+
+#pragma data_seg(".CRT$XTA")
+_PVFV __xt_a[] = { NULL };
+
+
+#pragma data_seg(".CRT$XTZ")
+_PVFV __xt_z[] = { NULL };
+
+#pragma comment(linker, "/merge:.CRT=.rdata")
+
+#pragma data_seg() /* reset */
+
+_PVFV *__onexitbegin;
+_PVFV *__onexitend;
+
+static void
+_initterm (
+ _PVFV * pfbegin,
+ _PVFV * pfend
+ )
+{
+ /*
+ * walk the table of function pointers from the bottom up, until
+ * the end is encountered. Do not skip the first entry. The initial
+ * value of pfbegin points to the first valid entry. Do not try to
+ * execute what pfend points to. Only entries before pfend are valid.
+ */
+ while ( pfbegin < pfend ) {
+ /*
+ * if current table entry is non-NULL, call thru it.
+ */
+ if ( *pfbegin != NULL )
+ (**pfbegin)();
+ ++pfbegin;
+ }
+}
+
+void
+mainNoCRTStartup(
+ void
+ )
+{
+ __try {
+
+ // do C initializations
+ _initterm( __xi_a, __xi_z );
+ // do C++ initializations
+ _initterm( __xc_a, __xc_z );
+
+ main(1, 0, 0);
+
+ } __except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ }
+
+ __try {
+ // Do C++ terminators
+ _initterm(__onexitbegin, __onexitend);
+
+ // do pre-terminators
+ _initterm(__xp_a, __xp_z);
+
+ // do C terminiators
+ _initterm(__xt_a, __xt_z);
+
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+
+ }
+}
+
diff --git a/private/lsa/server/makefile.inc b/private/lsa/server/makefile.inc
new file mode 100644
index 000000000..278f256f2
--- /dev/null
+++ b/private/lsa/server/makefile.inc
@@ -0,0 +1,2 @@
+lsapmsgs.h msg00001.bin: lsapmsgs.mc
+ mc -v lsapmsgs.mc
diff --git a/private/lsa/server/oldstub.c b/private/lsa/server/oldstub.c
new file mode 100644
index 000000000..6912791c8
--- /dev/null
+++ b/private/lsa/server/oldstub.c
@@ -0,0 +1,399 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ oldstub.c
+
+Abstract:
+
+ This file contains functions generated by midl v1.0. These
+ function were designed to only be called by the stubs,
+ these paticular functions are called by user code. This
+ file is needed in order to compile lsa with midl v2.0 which
+ doesn't generated these paticular functions anymore.
+
+Author:
+
+ Mario Goertzel (MarioGo) Jan 10, 1994
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+
+--*/
+
+#include <lsarpc.h>
+
+/* routine that frees graph for struct _STRING */
+void _fgs__STRING (STRING * _source)
+ {
+ if (_source->Buffer !=0)
+ {
+ MIDL_user_free((void *)(_source->Buffer));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_SID_INFORMATION */
+void _fgs__LSAPR_SID_INFORMATION (LSAPR_SID_INFORMATION * _source)
+ {
+ if (_source->Sid !=0)
+ {
+ MIDL_user_free((void *)(_source->Sid));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_SID_ENUM_BUFFER */
+void _fgs__LSAPR_SID_ENUM_BUFFER (LSAPR_SID_ENUM_BUFFER * _source)
+ {
+ if (_source->SidInfo !=0)
+ {
+ {
+ unsigned long _sym5;
+ for (_sym5 = 0; _sym5 < (unsigned long )(0 + _source->Entries); _sym5++)
+ {
+ _fgs__LSAPR_SID_INFORMATION ((LSAPR_SID_INFORMATION *)&_source->SidInfo[_sym5]);
+ }
+ }
+ MIDL_user_free((void *)(_source->SidInfo));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_ACCOUNT_INFORMATION */
+void _fgs__LSAPR_ACCOUNT_INFORMATION (LSAPR_ACCOUNT_INFORMATION * _source)
+ {
+ if (_source->Sid !=0)
+ {
+ MIDL_user_free((void *)(_source->Sid));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_ACCOUNT_ENUM_BUFFER */
+void _fgs__LSAPR_ACCOUNT_ENUM_BUFFER (LSAPR_ACCOUNT_ENUM_BUFFER * _source)
+ {
+ if (_source->Information !=0)
+ {
+ {
+ unsigned long _sym11;
+ for (_sym11 = 0; _sym11 < (unsigned long )(0 + _source->EntriesRead); _sym11++)
+ {
+ _fgs__LSAPR_ACCOUNT_INFORMATION ((LSAPR_ACCOUNT_INFORMATION *)&_source->Information[_sym11]);
+ }
+ }
+ MIDL_user_free((void *)(_source->Information));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_UNICODE_STRING */
+void _fgs__LSAPR_UNICODE_STRING (LSAPR_UNICODE_STRING * _source)
+ {
+ if (_source->Buffer !=0)
+ {
+ MIDL_user_free((void *)(_source->Buffer));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_SECURITY_DESCRIPTOR */
+void _fgs__LSAPR_SECURITY_DESCRIPTOR (LSAPR_SECURITY_DESCRIPTOR * _source)
+ {
+ if (_source->Owner !=0)
+ {
+ MIDL_user_free((void *)(_source->Owner));
+ }
+ if (_source->Group !=0)
+ {
+ MIDL_user_free((void *)(_source->Group));
+ }
+ if (_source->Sacl !=0)
+ {
+ MIDL_user_free((void *)(_source->Sacl));
+ }
+ if (_source->Dacl !=0)
+ {
+ MIDL_user_free((void *)(_source->Dacl));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_SR_SECURITY_DESCRIPTOR */
+void _fgs__LSAPR_SR_SECURITY_DESCRIPTOR (LSAPR_SR_SECURITY_DESCRIPTOR * _source)
+ {
+ if (_source->SecurityDescriptor !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_POLICY_PRIVILEGE_DEF */
+void _fgs__LSAPR_POLICY_PRIVILEGE_DEF (LSAPR_POLICY_PRIVILEGE_DEF * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->Name);
+ }
+
+ /* routine that frees graph for struct _LSAPR_PRIVILEGE_ENUM_BUFFER */
+void _fgs__LSAPR_PRIVILEGE_ENUM_BUFFER (LSAPR_PRIVILEGE_ENUM_BUFFER * _source)
+ {
+ if (_source->Privileges !=0)
+ {
+ {
+ unsigned long _sym25;
+ for (_sym25 = 0; _sym25 < (unsigned long )(0 + _source->Entries); _sym25++)
+ {
+ _fgs__LSAPR_POLICY_PRIVILEGE_DEF ((LSAPR_POLICY_PRIVILEGE_DEF *)&_source->Privileges[_sym25]);
+ }
+ }
+ MIDL_user_free((void *)(_source->Privileges));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_OBJECT_ATTRIBUTES */
+void _fgs__LSAPR_OBJECT_ATTRIBUTES (LSAPR_OBJECT_ATTRIBUTES * _source)
+ {
+ if (_source->RootDirectory !=0)
+ {
+ MIDL_user_free((void *)(_source->RootDirectory));
+ }
+ if (_source->ObjectName !=0)
+ {
+ _fgs__STRING ((STRING *)_source->ObjectName);
+ MIDL_user_free((void *)(_source->ObjectName));
+ }
+ if (_source->SecurityDescriptor !=0)
+ {
+ _fgs__LSAPR_SECURITY_DESCRIPTOR ((LSAPR_SECURITY_DESCRIPTOR *)_source->SecurityDescriptor);
+ MIDL_user_free((void *)(_source->SecurityDescriptor));
+ }
+ if (_source->SecurityQualityOfService !=0)
+ {
+ MIDL_user_free((void *)(_source->SecurityQualityOfService));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_CR_CIPHER_VALUE */
+void _fgs__LSAPR_CR_CIPHER_VALUE (LSAPR_CR_CIPHER_VALUE * _source)
+ {
+ if (_source->Buffer !=0)
+ {
+ MIDL_user_free((void *)(_source->Buffer));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_TRUST_INFORMATION */
+void _fgs__LSAPR_TRUST_INFORMATION (LSAPR_TRUST_INFORMATION * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->Name);
+ if (_source->Sid !=0)
+ {
+ MIDL_user_free((void *)(_source->Sid));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_TRUSTED_ENUM_BUFFER */
+void _fgs__LSAPR_TRUSTED_ENUM_BUFFER (LSAPR_TRUSTED_ENUM_BUFFER * _source)
+ {
+ if (_source->Information !=0)
+ {
+ {
+ unsigned long _sym31;
+ for (_sym31 = 0; _sym31 < (unsigned long )(0 + _source->EntriesRead); _sym31++)
+ {
+ _fgs__LSAPR_TRUST_INFORMATION ((LSAPR_TRUST_INFORMATION *)&_source->Information[_sym31]);
+ }
+ }
+ MIDL_user_free((void *)(_source->Information));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_REFERENCED_DOMAIN_LIST */
+void _fgs__LSAPR_REFERENCED_DOMAIN_LIST (LSAPR_REFERENCED_DOMAIN_LIST * _source)
+ {
+ if (_source->Domains !=0)
+ {
+ {
+ unsigned long _sym37;
+ for (_sym37 = 0; _sym37 < (unsigned long )(0 + _source->Entries); _sym37++)
+ {
+ _fgs__LSAPR_TRUST_INFORMATION ((LSAPR_TRUST_INFORMATION *)&_source->Domains[_sym37]);
+ }
+ }
+ MIDL_user_free((void *)(_source->Domains));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_TRANSLATED_SIDS */
+void _fgs__LSAPR_TRANSLATED_SIDS (LSAPR_TRANSLATED_SIDS * _source)
+ {
+ if (_source->Sids !=0)
+ {
+ MIDL_user_free((void *)(_source->Sids));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_TRANSLATED_NAME */
+void _fgs__LSAPR_TRANSLATED_NAME (LSAPR_TRANSLATED_NAME * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->Name);
+ }
+
+ /* routine that frees graph for struct _LSAPR_TRANSLATED_NAMES */
+void _fgs__LSAPR_TRANSLATED_NAMES (LSAPR_TRANSLATED_NAMES * _source)
+ {
+ if (_source->Names !=0)
+ {
+ {
+ unsigned long _sym58;
+ for (_sym58 = 0; _sym58 < (unsigned long )(0 + _source->Entries); _sym58++)
+ {
+ _fgs__LSAPR_TRANSLATED_NAME ((LSAPR_TRANSLATED_NAME *)&_source->Names[_sym58]);
+ }
+ }
+ MIDL_user_free((void *)(_source->Names));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_POLICY_ACCOUNT_DOM_INFO */
+void _fgs__LSAPR_POLICY_ACCOUNT_DOM_INFO (LSAPR_POLICY_ACCOUNT_DOM_INFO * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->DomainName);
+ if (_source->DomainSid !=0)
+ {
+ MIDL_user_free((void *)(_source->DomainSid));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_POLICY_PRIMARY_DOM_INFO */
+void _fgs__LSAPR_POLICY_PRIMARY_DOM_INFO (LSAPR_POLICY_PRIMARY_DOM_INFO * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->Name);
+ if (_source->Sid !=0)
+ {
+ MIDL_user_free((void *)(_source->Sid));
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_POLICY_PD_ACCOUNT_INFO */
+void _fgs__LSAPR_POLICY_PD_ACCOUNT_INFO (LSAPR_POLICY_PD_ACCOUNT_INFO * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->Name);
+ }
+
+ /* routine that frees graph for struct _LSAPR_POLICY_REPLICA_SRCE_INFO */
+void _fgs__LSAPR_POLICY_REPLICA_SRCE_INFO (LSAPR_POLICY_REPLICA_SRCE_INFO * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->ReplicaSource);
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->ReplicaAccountName);
+ }
+
+ /* routine that frees graph for struct _LSAPR_POLICY_AUDIT_EVENTS_INFO */
+void _fgs__LSAPR_POLICY_AUDIT_EVENTS_INFO (LSAPR_POLICY_AUDIT_EVENTS_INFO * _source)
+ {
+ if (_source->EventAuditingOptions !=0)
+ {
+ MIDL_user_free((void *)(_source->EventAuditingOptions));
+ }
+ }
+
+ /* routine that frees graph for union _LSAPR_POLICY_INFORMATION */
+void _fgu__LSAPR_POLICY_INFORMATION (LSAPR_POLICY_INFORMATION * _source, POLICY_INFORMATION_CLASS _branch)
+ {
+ switch (_branch)
+ {
+ case PolicyAuditLogInformation :
+ {
+ break;
+ }
+ case PolicyAuditEventsInformation :
+ {
+ _fgs__LSAPR_POLICY_AUDIT_EVENTS_INFO ((LSAPR_POLICY_AUDIT_EVENTS_INFO *)&_source->PolicyAuditEventsInfo);
+ break;
+ }
+ case PolicyPrimaryDomainInformation :
+ {
+ _fgs__LSAPR_POLICY_PRIMARY_DOM_INFO ((LSAPR_POLICY_PRIMARY_DOM_INFO *)&_source->PolicyPrimaryDomainInfo);
+ break;
+ }
+ case PolicyAccountDomainInformation :
+ {
+ _fgs__LSAPR_POLICY_ACCOUNT_DOM_INFO ((LSAPR_POLICY_ACCOUNT_DOM_INFO *)&_source->PolicyAccountDomainInfo);
+ break;
+ }
+ case PolicyPdAccountInformation :
+ {
+ _fgs__LSAPR_POLICY_PD_ACCOUNT_INFO ((LSAPR_POLICY_PD_ACCOUNT_INFO *)&_source->PolicyPdAccountInfo);
+ break;
+ }
+ case PolicyLsaServerRoleInformation :
+ {
+ break;
+ }
+ case PolicyReplicaSourceInformation :
+ {
+ _fgs__LSAPR_POLICY_REPLICA_SRCE_INFO ((LSAPR_POLICY_REPLICA_SRCE_INFO *)&_source->PolicyReplicaSourceInfo);
+ break;
+ }
+ case PolicyDefaultQuotaInformation :
+ {
+ break;
+ }
+ case PolicyModificationInformation :
+ {
+ break;
+ }
+ case PolicyAuditFullSetInformation :
+ {
+ break;
+ }
+ case PolicyAuditFullQueryInformation :
+ {
+ break;
+ }
+ }
+ }
+
+ /* routine that frees graph for struct _LSAPR_TRUSTED_DOMAIN_NAME_INFO */
+void _fgs__LSAPR_TRUSTED_DOMAIN_NAME_INFO (LSAPR_TRUSTED_DOMAIN_NAME_INFO * _source)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->Name);
+ }
+
+ /* routine that frees graph for struct _LSAPR_TRUSTED_CONTROLLERS_INFO */
+void _fgs__LSAPR_TRUSTED_CONTROLLERS_INFO (LSAPR_TRUSTED_CONTROLLERS_INFO * _source)
+ {
+ if (_source->Names !=0)
+ {
+ {
+ unsigned long _sym69;
+ for (_sym69 = 0; _sym69 < (unsigned long )(0 + _source->Entries); _sym69++)
+ {
+ _fgs__LSAPR_UNICODE_STRING ((LSAPR_UNICODE_STRING *)&_source->Names[_sym69]);
+ }
+ }
+ MIDL_user_free((void *)(_source->Names));
+ }
+ }
+
+ /* routine that frees graph for union _LSAPR_TRUSTED_DOMAIN_INFO */
+void _fgu__LSAPR_TRUSTED_DOMAIN_INFO (LSAPR_TRUSTED_DOMAIN_INFO * _source, TRUSTED_INFORMATION_CLASS _branch)
+ {
+ switch (_branch)
+ {
+ case TrustedDomainNameInformation :
+ {
+ _fgs__LSAPR_TRUSTED_DOMAIN_NAME_INFO ((LSAPR_TRUSTED_DOMAIN_NAME_INFO *)&_source->TrustedDomainNameInfo);
+ break;
+ }
+ case TrustedControllersInformation :
+ {
+ _fgs__LSAPR_TRUSTED_CONTROLLERS_INFO ((LSAPR_TRUSTED_CONTROLLERS_INFO *)&_source->TrustedControllersInfo);
+ break;
+ }
+ case TrustedPosixOffsetInformation :
+ {
+ break;
+ }
+ }
+ }
+
diff --git a/private/lsa/server/regnames.txt b/private/lsa/server/regnames.txt
new file mode 100644
index 000000000..28535f7d9
--- /dev/null
+++ b/private/lsa/server/regnames.txt
@@ -0,0 +1,123 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ regnames.txt
+
+Abstract:
+
+ This file describes the Configuration Registry namespace used to
+ back-store the LSA database.
+
+
+Author:
+
+ Scott Birrell (ScottBi) 11-June-1991
+
+Revision History:
+
+General:
+
+The Local Security Authority (LSA) stores the Local Security Policy (LSP)
+of a system in a subtree of the Configuration Registry. The Registry
+consists of a hierarchy of nodes called "Keys". Each Key can have associated
+with it data called the "Value" of the Key. A Key Value can also have a
+"Key Value Type" associated with it when the value is set.
+
+A full Registry Keyname has the following general syntax:
+
+\Registry\KeyNameE\KeyNameE\..\KeyNameE.
+
+where each KeyNameE is an "element" of the Key name. We usually use the term
+"Keyname" to mean any element of the full name as indicated above. Within the
+Registry, the Local Security Policy occupies the subtree
+
+\Registry\RLM\Security\Protectd\LSP
+
+which is so named because it is the Local Security Policy subtree of the
+Protected part of the Security subtree of the Registry's Local Machine
+information.
+
+This file describes the content of Keys at all levels within this LSP. We
+first introduce some notation.
+
+Notational Conventions:
+
+o Xxx is the Unicode name of a Registry Key element. For example,
+ "PasswordExpires".
+
+o (Xxx) is a description of a Registry Key's name. For example,
+ "(UserName)" might indicate that the Key name is a user's name.
+
+o [kvt,Value] kvt is the Key Value Type, and Value describes the
+ value of a Registry Key. If no specific Key Value Type is used,
+ then [,Value] references just the value. If the Key has a
+ Key Value Type, but no Key Value, then [kvt,] notation is used.
+
+o Individual Keys or Key Values may be referenced in the description
+ as the following examples show:
+
+ LSP/Accounts/(AcctName)
+ - references a particular Account Name.
+
+ LSP/Accounts/(AcctName)[,Rid]
+ - references a value of a named Key.
+
+ .../(AcctName) or
+ .../(AcctName)[Rid] may also be used as a shorthand notation in which
+ the first part of the Key has been omitted.
+
+ NOTE: In several instances, a RID (Relative Id) is used as a Key name. In
+ this case an ASCII conversion of the ULONG value is used. The name is
+ printable and contains no zero bytes.
+
+LSP Database Struture:
+
+
+Using the above notation, the structure of the Registry namespace used to
+back-store the LSA's Local Security Policy (PSP) Database is as follows:
+
+ LSP
+ --+-
+ +-- SecDesc [,SecurityDescriptor]
+ +-- Domain [,SidValue]
+ +-- AdminMod [,AdmininstrationModeValue]
+ +-- OperMode [,OperationalModeValue]
+ +-- Accounts [Count,]
+ ----+
+ +-- (AccountName1) [AccountRid1,]
+ | (...)
+ +-- (AccountNameN) [AccountRidN,]
+ |
+ +-- (AccountRid1) [,AccountName1]
+ | (...)
+ +-- (AccountRidN) [,AccountNameN]
+
+ BltAccts
+ ----+--
+ +-- (BuiltInManagerAccountName) [AccountRidMGR,]
+ | (...)
+ +-- (BuiltInGuestAccountName) [AccountRidGST,]
+ |
+ +-- (AccountRidMGR) [,BuiltInManagerAccountName]
+ | (...)
+ +-- (AccountRidGST) [,BuiltInGuestAccountName]
+
+
+ The structure under each User or Group Account (AccountRid) Key is
+ is as follows:
+
+ (AccountRid)[PosixId,AccountName]
+ ----+---------
+ +-- SecDesc [,SecurityDescriptor]
+ +-- Privilgs [,PrivilegeSet]
+ +-- Quotas [,QuotaLimits]
+
+ The structure under each Built-in Account (AccountRid) is as follows:
+
+ (BuiltInAccountName) [AccountRid,]
+ ----+---------
+ +-- SecDesc [,SecurityDescriptor]
+--*/
diff --git a/private/lsa/server/rpcinit.c b/private/lsa/server/rpcinit.c
new file mode 100644
index 000000000..b116e8941
--- /dev/null
+++ b/private/lsa/server/rpcinit.c
@@ -0,0 +1,193 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ rpcinit.c
+
+Abstract:
+
+ LSA - RPC Server Initialization
+
+Author:
+
+ Scott Birrell (ScottBi) April 29, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsasrvp.h"
+
+
+NTSTATUS
+LsapRPCInit(
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs the initialization of the RPC server in the LSA
+ subsystem. Clients such as the Local Security Manager on this or some
+ other machine will then be able to call the LSA API that use RPC .
+
+Arguments:
+
+ None
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ All Result Code returned are from called routines.
+
+Environment:
+
+ User Mode
+--*/
+
+{
+ NTSTATUS NtStatus;
+ LPWSTR ServiceName;
+
+ //
+ // Publish the Lsa server interface package...
+ //
+ //
+ // NOTE: Now all RPC servers in lsass.exe (now winlogon) share the same
+ // pipe name. However, in order to support communication with
+ // version 1.0 of WinNt, it is necessary for the Client Pipe name
+ // to remain the same as it was in version 1.0. Mapping to the new
+ // name is performed in the Named Pipe File System code.
+ //
+ //
+
+ ServiceName = L"lsass";
+ NtStatus = RpcpAddInterface( ServiceName, lsarpc_ServerIfHandle);
+
+ if (!NT_SUCCESS(NtStatus)) {
+
+ LsapLogError(
+ "LSASS: Could Not Start RPC Server.\n"
+ " Failing to initialize LSA Server.\n",
+ NtStatus
+ );
+ }
+
+ return(NtStatus);
+}
+
+
+VOID LSAPR_HANDLE_rundown(
+ LSAPR_HANDLE LsaHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the server RPC runtime to run down a
+ Context Handle.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN AcquiredLock = FALSE;
+
+ Status = LsapDbAcquireLock();
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LsaHandleRundownError;
+ }
+
+ AcquiredLock = TRUE;
+
+ //
+ // Verify that the supplied LsaHandle still exists.
+ //
+
+ Status = LsapDbVerifyHandle(
+ LsaHandle,
+ LSAP_DB_ADMIT_DELETED_OBJECT_HANDLES,
+ NullObject
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("lsarpc_LSAPR_HANDLE_rundown: Invalid Handle 0x%lx\n", LsaHandle));
+ goto LsaHandleRundownError;
+ }
+
+ //
+ // Close and free the handle. Since the container handle reference
+ // count includes one reference for every reference made to the
+ // target handle, the container's reference count will be decremented
+ // by n where n is the reference count in the target handle.
+ //
+
+ Status = LsapDbCloseObject(
+ &LsaHandle,
+ LSAP_DB_FREE_HANDLE | LSAP_DB_DEREFERENCE_CONTR
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto LsaHandleRundownError;
+ }
+
+LsaHandleRundownFinish:
+
+ //
+ // If necessary, free the Lsa Database Lock
+ //
+
+ if (AcquiredLock) {
+
+ LsapDbReleaseLock();
+ AcquiredLock = FALSE;
+ }
+
+ return;
+
+LsaHandleRundownError:
+
+ goto LsaHandleRundownFinish;
+}
+
+
+VOID PLSA_ENUMERATION_HANDLE_rundown(
+ PLSA_ENUMERATION_HANDLE LsaHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the server RPC runtime to run down a
+ Context Handle.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ DBG_UNREFERENCED_PARAMETER(LsaHandle);
+
+ return;
+}
diff --git a/private/lsa/server/sepriv.c b/private/lsa/server/sepriv.c
new file mode 100644
index 000000000..71383c1bd
--- /dev/null
+++ b/private/lsa/server/sepriv.c
@@ -0,0 +1,519 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ sepriv.c
+
+Abstract:
+
+ Security Runtime Library Privilege Routines
+
+ (Temporary copy of \nt\private\ntos\rtl\sepriv.c to allow
+ ntdailybld version of ntdll.dll to be used)
+
+ These routines perform operations with privilege sets
+
+Author:
+
+ Scott Birrell (ScottBi) June 17, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <string.h>
+#include "lsasrvp.h"
+
+#define LsapRtlEqualPrivileges(FirstPrivilege, SecondPrivilege) \
+ (RtlEqualLuid(&(FirstPrivilege)->Luid, &(SecondPrivilege)->Luid))
+
+NTSTATUS
+LsapRtlAddPrivileges(
+ IN OPTIONAL PPRIVILEGE_SET ExistingPrivileges,
+ IN PPRIVILEGE_SET PrivilegesToAdd,
+ IN OPTIONAL PPRIVILEGE_SET UpdatedPrivileges,
+ IN OUT PULONG UpdatedPrivilegesSize,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function adds and/or updates privileges in a privilege set. The
+ existing privilege set is unaltered, a new privilege set being generated.
+ The memory for the new privilege set must already be allocated by the
+ caller. To assist in calculating the size of buffer required, the
+ routine may be called in 'query' mode by supplying buffer size of 0. In
+ this mode, the amount of memory required is returned and no copying
+ takes place.
+
+ WARNING: Privileges within each privilege set must all be distinct, that
+ is, there must not be two privileges in the same set having the same LUID.
+
+Arguments:
+
+ ExistingPrivileges - Optional pointer to existing privilege set. If
+ NULL is specified, all of the privileges specified by the
+ PrivilegesToAdd parameter will be added.
+
+ PrivilegesToAdd - Pointer to privilege set specifying privileges to
+ be added. The attributes of privileges in this set that also exist
+ in the ExistingPrivileges set supersede the attributes therein.
+
+ UpdatedPrivileges - Pointer to buffer that will receive the updated
+ privilege set. Care must be taken to ensure that UpdatedPrivileges
+ occupies memory disjoint from that occupied by ExistingPrivileges
+ and PrivilegesToAdd. This parameter may be specified as NULL if
+ the input size given for UpdatedPrivilegesSize = 0.
+
+ UpdatedPrivilegesSize - Pointer to variable that contains a size.
+ On input, the size is the size of the UpdatedPrivileges buffer
+ given (if any) or 0. If a zero size is given, no copying of
+ privileges takes place. On output, the size of the output buffer
+ required or used is returned.
+
+ Options - Specifies optional actions.
+
+ RTL_COMBINE_PRIVILEGE_ATTRIBUTES - If the two privilege sets have
+ privileges in common, combine the attributes
+
+ RTL_SUPERSEDE_PRIVILEGE_ATTRIBUTES - If the two privilege sets
+ have privileges in common, supersede the existing attributes
+ with those specified in PrivilegesToAdd.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ - STATUS_BUFFER_OVERFLOW - This warning indicates that the buffer
+ output privilege set overflowed. Caller should test for this
+ warning and if received, allocate a buffer of sufficient size
+ and repeat the call.
+
+Environment:
+
+ User or Kernel modes.
+
+--*/
+
+{
+ ULONG ExistingIndex;
+ ULONG UpdatedIndex;
+ PLUID_AND_ATTRIBUTES PrivilegeToCopy;
+ PLUID_AND_ATTRIBUTES Privilege;
+ LUID_AND_ATTRIBUTES TmpPrivilege;
+ ULONG UpdatedSizeUsed =
+ sizeof (PRIVILEGE_SET) - sizeof (LUID_AND_ATTRIBUTES);
+ ULONG AddIndex = 0L;
+ ULONG AddedPrivilegesCopied = 0L;
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Verify that mandatory parameters have been specified.
+ // specified.
+ //
+
+ if (PrivilegesToAdd == NULL ||
+ UpdatedPrivilegesSize == NULL) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Validate the Options parameter.
+ //
+
+ if ((Options != RTL_SUPERSEDE_PRIVILEGE_ATTRIBUTES) &&
+ (Options != RTL_COMBINE_PRIVILEGE_ATTRIBUTES)) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // If there are no existing privileges, add all of the privileges.
+ //
+
+ if (ExistingPrivileges == NULL) {
+
+ for(AddIndex = 0;
+ AddIndex < PrivilegesToAdd->PrivilegeCount;
+ AddIndex++) {
+
+ if (*UpdatedPrivilegesSize != 0) {
+
+ if (UpdatedSizeUsed + sizeof(LUID_AND_ATTRIBUTES)
+ <= *UpdatedPrivilegesSize) {
+
+ UpdatedPrivileges->Privilege[AddIndex] =
+ PrivilegesToAdd->Privilege[AddIndex];
+ }
+
+ } else {
+
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ //
+ // Update size even if buffer overflow occurs.
+ //
+
+ UpdatedSizeUsed += sizeof(LUID_AND_ATTRIBUTES);
+ }
+
+ //
+ // Set the header fields in the output privilege set
+ // if necessary.
+ //
+
+ if (UpdatedSizeUsed <= *UpdatedPrivilegesSize) {
+
+ UpdatedPrivileges->PrivilegeCount =
+ PrivilegesToAdd->PrivilegeCount;
+ UpdatedPrivileges->Control = PrivilegesToAdd->Control;
+ }
+
+ //
+ // Return the size needed.
+ //
+
+ *UpdatedPrivilegesSize = UpdatedSizeUsed;
+ return Status;
+ }
+
+ //
+ // An existing privilege set has been specified. Scan this set,
+ // looking up each privilege in the add set by matching LUIDs. If no
+ // match, copy the existing LUID_AND_ATTRIBUTES, else copy the new.
+ //
+
+ for (ExistingIndex = 0, UpdatedIndex = 0;
+ ExistingIndex < ExistingPrivileges->PrivilegeCount;
+ ExistingIndex++, UpdatedIndex++) {
+
+ //
+ // By default, we copy the next existing privilege to the
+ // output array.
+ //
+
+ PrivilegeToCopy = &(ExistingPrivileges->Privilege[ExistingIndex]);
+
+ //
+ // Search for the existing privilege to see if there is a
+ // new version of it in the Add array. If so, either supersede
+ // the old version's attributes with those in the PrivilegesToAdd
+ // array, or combine them, depending on the Options parameter.
+ //
+
+ if ((Privilege = LsapRtlGetPrivilege(
+ PrivilegeToCopy,
+ PrivilegesToAdd
+ )) != NULL) {
+
+ //
+ // The same privilege appears in both privilege sets. If
+ // we are to supersede the privilege attributes, set the
+ // source privilege pointer to point to the privilege in
+ // the PrivilegesToAdd array, else, make a copy of that
+ // privilege containing the combined privilege attributes and
+ // point to the copy.
+ //
+
+ if (Options & RTL_SUPERSEDE_PRIVILEGE_ATTRIBUTES) {
+
+ PrivilegeToCopy = Privilege;
+ AddedPrivilegesCopied++;
+
+ } else {
+
+ TmpPrivilege = *PrivilegeToCopy;
+ TmpPrivilege.Attributes |= Privilege->Attributes;
+ PrivilegeToCopy = &TmpPrivilege;
+ }
+ }
+
+ //
+ // If room, copy the privilege to the output buffer, else
+ // set overflow status. Continue looping to complete calculation
+ // of size needed.
+ //
+
+ UpdatedSizeUsed += sizeof (LUID_AND_ATTRIBUTES);
+
+ if (UpdatedSizeUsed <= *UpdatedPrivilegesSize) {
+
+ UpdatedPrivileges->Privilege[UpdatedIndex] =
+ *PrivilegeToCopy;
+
+ } else {
+
+ Status = STATUS_BUFFER_OVERFLOW; // This is a warning
+ }
+ }
+
+ //
+ // Now scan the Add list looking for new privileges. Append these
+ // to the output array / update used array size. Stop when we know
+ // all entries have been accounted for.
+
+
+ for (AddIndex = 0;
+ AddIndex < PrivilegesToAdd->PrivilegeCount;
+ AddIndex++) {
+
+ //
+ // Search the existing privilege array for this privilege. If we
+ // do not find it, we know it has not been copied to the output or
+ // counted.
+ //
+
+ if ((Privilege = LsapRtlGetPrivilege(
+ &(PrivilegesToAdd->Privilege[AddIndex]),
+ ExistingPrivileges
+ )) == NULL) {
+
+ UpdatedSizeUsed += sizeof (LUID_AND_ATTRIBUTES);
+
+ if (UpdatedSizeUsed <= *UpdatedPrivilegesSize) {
+
+ UpdatedPrivileges->Privilege[UpdatedIndex] =
+ PrivilegesToAdd->Privilege[AddIndex];
+
+ } else {
+
+ Status = STATUS_BUFFER_OVERFLOW; // This is a warning
+ }
+
+ UpdatedIndex++;
+
+ if (++AddedPrivilegesCopied ==
+ PrivilegesToAdd->PrivilegeCount) {
+
+ break;
+ }
+ }
+ }
+
+ //
+ // Set the header fields in the output privilege set
+ // if necessary.
+ //
+
+ if (UpdatedSizeUsed <= *UpdatedPrivilegesSize) {
+
+ UpdatedPrivileges->PrivilegeCount = UpdatedIndex;
+ UpdatedPrivileges->Control = PrivilegesToAdd->Control;
+ }
+
+ //
+ // Return the Updated Size Used/Needed
+ //
+
+ *UpdatedPrivilegesSize = UpdatedSizeUsed;
+
+ return Status;
+}
+
+
+
+NTSTATUS
+LsapRtlRemovePrivileges(
+ IN PPRIVILEGE_SET ExistingPrivileges,
+ IN PPRIVILEGE_SET PrivilegesToRemove,
+ IN OPTIONAL PPRIVILEGE_SET UpdatedPrivileges,
+ IN PULONG UpdatedPrivilegesSize
+ )
+
+/*++
+
+Routine Description:
+
+ This function removes privileges in a privilege set. The existing
+ privilege set is unaltered, a new privilege set being generated.
+
+ WARNING: Privileges within each privilege set must all be distinct, that
+ is, there must not be two privileges in the same set having the same LUID.
+
+Arguments:
+
+ ExistingPrivileges - Pointer to existing privilege set
+
+ PrivilegesToRemove - Pointer to privilege set specifying privileges to
+ be removed. The privilege attributes are ignored. Privileges
+ in the PrivilegesToRemove set that are not present in the
+ ExistingPrivileges set will be ignored.
+
+ UpdatedPrivileges - Pointer to buffer that will receive the updated
+ privilege set. Care must be taken to ensure that UpdatedPrivileges
+ occupies memory disjoint from that occupied by ExistingPrivileges
+ and PrivilegesToChange.
+
+ UpdatedPrivilegesSize - Pointer to variable that contains a size.
+ On input, the size is the size of the UpdatedPrivileges buffer
+ (if any). On output, the size is the size needed/used for the
+ updated privilege set. If the updated privilege set will be
+ NULL, 0 is returned.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ - STATUS_INVALID_PARAMETER - Invalid parameter(s)
+ Mandatory parameters not specified
+ UpdatedPrivileges buffer not specified (except on
+ query-only calls
+
+ - STATUS_BUFFER_OVERFLOW - This warning indicates that the buffer
+ output privilege set overflowed. Caller should test for this
+ warning and if received, allocate a buffer of sufficient size
+ and repeat the call.
+
+Environment:
+
+ User or Kernel modes.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLUID_AND_ATTRIBUTES Privilege;
+ ULONG ExistingIndex;
+ ULONG UpdatedIndex = 0L;
+ ULONG UpdatedSizeUsed =
+ sizeof (PRIVILEGE_SET) - sizeof (LUID_AND_ATTRIBUTES);
+
+ //
+ // Verify that mandatory parameters have been specified.
+ //
+
+ if (ExistingPrivileges == NULL ||
+ PrivilegesToRemove == NULL) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Scan through the privileges in the existing privilege set. Look up
+ // each privilege in the list of privileges to be removed. If the
+ // privilege is not found there, it is to be retained, so copy it
+ // to the output buffer/count it.
+ //
+
+ for (ExistingIndex = 0;
+ ExistingIndex < ExistingPrivileges->PrivilegeCount;
+ ExistingIndex++) {
+
+ //
+ // If the next privilege is not in the set to be deleted,
+ // copy it to output/count it
+ //
+
+ if ((Privilege = LsapRtlGetPrivilege(
+ &(ExistingPrivileges->Privilege[ExistingIndex]),
+ PrivilegesToRemove)
+ ) == NULL) {
+
+ UpdatedSizeUsed += sizeof(LUID_AND_ATTRIBUTES);
+
+ if (UpdatedSizeUsed <= *UpdatedPrivilegesSize) {
+
+ UpdatedPrivileges->Privilege[UpdatedIndex] =
+ ExistingPrivileges->Privilege[ExistingIndex];
+
+ } else {
+
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ UpdatedIndex++;
+ }
+ }
+
+ //
+ // Set the header fields in the output privilege set
+ // if necessary.
+ //
+
+ if (UpdatedSizeUsed <= *UpdatedPrivilegesSize) {
+
+ UpdatedPrivileges->PrivilegeCount = UpdatedIndex;
+ UpdatedPrivileges->Control = ExistingPrivileges->Control;
+ }
+
+ //
+ // Return the size of the output privilege set. If the output
+ // privilege set is/will be NULL, return 0 for the size.
+ //
+
+ if (UpdatedSizeUsed ==
+ sizeof (PRIVILEGE_SET) - sizeof (LUID_AND_ATTRIBUTES)) {
+
+ UpdatedSizeUsed = 0;
+ }
+
+ *UpdatedPrivilegesSize = UpdatedSizeUsed;
+ return Status;
+}
+
+
+PLUID_AND_ATTRIBUTES
+LsapRtlGetPrivilege(
+ IN PLUID_AND_ATTRIBUTES Privilege,
+ IN PPRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+WARNING: THIS ROUTINE IS NOT YET AVAILABLE
+
+ This function locates a privilege in a privilege set. If found,
+ a pointer to the privilege wihtin the set is returned, otherwise NULL
+ is returned.
+
+Arguments:
+
+ Privilege - Pointer to the privilege to be looked up.
+
+ Privileges - Pointer to the privilege set to be scanned.
+
+Return Value:
+
+ PLUID_AND_ATTRIBUTES - If the privilege is found, a pointer to its
+ LUID and ATTRIBUTES structure within the privilege set is returned,
+ otherwise NULL is returned.
+
+Environment:
+
+ User or Kernel modes.
+
+--*/
+
+{
+ ULONG PrivilegeIndex;
+
+ for (PrivilegeIndex = 0;
+ PrivilegeIndex < Privileges->PrivilegeCount;
+ PrivilegeIndex++) {
+
+ if (LsapRtlEqualPrivileges(
+ Privilege,
+ &(Privileges->Privilege[PrivilegeIndex])
+ )) {
+
+ return &(Privileges->Privilege[PrivilegeIndex]);
+ }
+ }
+
+ //
+ // The privilege was no found. Return NULL
+ //
+
+ return NULL;
+}
diff --git a/private/lsa/server/services.c b/private/lsa/server/services.c
new file mode 100644
index 000000000..acc426f6f
--- /dev/null
+++ b/private/lsa/server/services.c
@@ -0,0 +1,458 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ services.c
+
+Abstract:
+
+ This is the service dispatcher for the security process. It contains
+ the service dispatcher initialization routine and the routines to
+ load the DLL for the individual serices and execute them.
+
+Author:
+
+ Rajen Shah (rajens) 11-Apr-1991
+
+[Environment:]
+
+ User Mode - Win32
+
+Revision History:
+
+ 11-Apr-1991 RajenS
+ created
+ 27-Sep-1991 JohnRo
+ More work toward UNICODE.
+ 24-Jan-1991 CliffV
+ Converted to be service dispatcher for the security process.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+#include <lmerr.h> // NERR_ and ERROR_ equates.
+#include <lmsname.h>
+#include <winsvc.h>
+#include <crypt.h>
+#include <ntsam.h>
+#include <logonmsv.h>
+
+#if DBG
+
+#define IF_DEBUG() if (TRUE)
+
+#else
+
+#define IF_DEBUG() if (FALSE)
+
+#endif
+
+#define IN_RANGE(SomeValue, SomeMin, SomeMax) \
+ ( ((SomeValue) >= (SomeMin)) && ((SomeValue) <= (SomeMax)) )
+
+//
+// SET_SERVICE_EXITCODE() sets the SomeApiStatus to NetCodeVariable
+// if it is within the NERR_BASE and NERR_MAX range. Otherwise,
+// Win32CodeVariable is set.
+//
+#define SET_SERVICE_EXITCODE(SomeApiStatus, Win32CodeVariable, NetCodeVariable) \
+ { \
+ if ((SomeApiStatus) == NERR_Success) { \
+ (Win32CodeVariable) = NO_ERROR; \
+ (NetCodeVariable) = NERR_Success; \
+ } else if (! IN_RANGE((SomeApiStatus), MIN_LANMAN_MESSAGE_ID, \
+ MAX_LANMAN_MESSAGE_ID)) { \
+ (Win32CodeVariable) = (DWORD) (SomeApiStatus); \
+ (NetCodeVariable) = (DWORD) (SomeApiStatus); \
+ } else { \
+ (Win32CodeVariable) = ERROR_SERVICE_SPECIFIC_ERROR; \
+ (NetCodeVariable) = (DWORD) (SomeApiStatus); \
+ } \
+ }
+
+
+typedef DWORD (*PNETLOGON_MAIN) ( \
+ IN DWORD dwNumServicesArgs, \
+ IN LPTSTR *lpServiceArgVectors \
+ );
+
+
+VOID
+DummyControlHandler(
+ IN DWORD opcode
+ )
+/*++
+
+Routine Description:
+
+ Process and respond to a control signal from the service controller.
+
+Arguments:
+
+ opcode - Supplies a value which specifies the action for the Netlogon
+ service to perform.
+
+Return Value:
+
+ None.
+
+ NOTE : this is a dummy handler, used to uninstall the netlogon service
+ when we unable to load netlogon dll.
+--*/
+{
+
+ IF_DEBUG() {
+ DbgPrint( "[Security Process] in control handler\n");
+ }
+
+ return;
+}
+
+
+VOID
+SrvLoadNetlogon (
+ IN DWORD dwNumServicesArgs,
+ IN LPTSTR *lpServiceArgVectors
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the 'main' routine for the netlogon service. It loads
+ Netlogon.dll (which contains the remainder of the service) and
+ calls the main entry point there.
+
+Arguments:
+
+ dwNumServicesArgs - Number of arguments in lpServiceArgVectors.
+
+ lpServiceArgVectors - Argument strings.
+
+Return Value:
+
+ return nothing.
+
+Note:
+
+
+--*/
+{
+ NET_API_STATUS NetStatus;
+ HANDLE NetlogonDllHandle = NULL;
+ PNETLOGON_MAIN NetlogonMain;
+ DWORD ReturnStatus;
+
+ SERVICE_STATUS_HANDLE ServiceHandle;
+ SERVICE_STATUS ServiceStatus;
+
+ //
+ // Load netlogon.dll
+ //
+
+ NetlogonDllHandle = LoadLibraryA( "Netlogon" );
+
+ if ( NetlogonDllHandle == NULL ) {
+ NetStatus = GetLastError();
+
+ IF_DEBUG() {
+
+ DbgPrint( "[Security process] "
+ "load library netlogon.dll failed %ld\n", NetStatus );
+ }
+
+ goto Cleanup;
+ }
+
+ //
+ // Find the main entry point for the netlogon service.
+ //
+
+ NetlogonMain = (PNETLOGON_MAIN)
+ GetProcAddress( NetlogonDllHandle, "NlNetlogonMain");
+
+ if ( NetlogonMain == NULL ) {
+ NetStatus = GetLastError();
+
+ IF_DEBUG() {
+
+ DbgPrint( "[Security process] GetProcAddress failed %ld\n",
+ NetStatus );
+ }
+
+ goto Cleanup;
+ }
+
+ //
+ // Call the Netlogon service.
+ //
+
+ ReturnStatus = (*NetlogonMain)(
+ dwNumServicesArgs,
+ lpServiceArgVectors
+ );
+
+ //
+ // Unload the library and return.
+ //
+
+ (VOID) FreeLibrary( NetlogonDllHandle );
+
+ return;
+
+Cleanup:
+
+ if ( NetlogonDllHandle != NULL ) {
+ (VOID) FreeLibrary( NetlogonDllHandle );
+ }
+
+ //
+ // register netlogon to service controller
+ //
+
+ ServiceHandle =
+ RegisterServiceCtrlHandler( SERVICE_NETLOGON, DummyControlHandler);
+
+ if (ServiceHandle != (SERVICE_STATUS_HANDLE) NULL) {
+
+ //
+ // inform service controller that the service can't start.
+ //
+
+ ServiceStatus.dwServiceType = SERVICE_WIN32;
+ ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE;
+ ServiceStatus.dwCheckPoint = 0;
+ ServiceStatus.dwWaitHint = 0;
+
+ SET_SERVICE_EXITCODE(
+ NetStatus,
+ ServiceStatus.dwWin32ExitCode,
+ ServiceStatus.dwServiceSpecificExitCode
+ );
+
+ if( !SetServiceStatus( ServiceHandle, &ServiceStatus ) ) {
+
+ IF_DEBUG() {
+
+ DbgPrint( "[Security process] SetServiceStatus failed %ld\n",
+ GetLastError() );
+ }
+ }
+
+ }
+ else {
+
+ IF_DEBUG() {
+
+ DbgPrint( "[Security process] "
+ "RegisterServiceCtrlHandler failed %ld\n",
+ GetLastError() );
+ }
+ }
+
+ return;
+}
+
+
+//
+// Dispatch table for all services. Passed to NetServiceStartCtrlDispatcher.
+//
+// Add new service entries here.
+//
+
+SERVICE_TABLE_ENTRY SecurityServiceDispatchTable[] = {
+ { SERVICE_NETLOGON, SrvLoadNetlogon },
+ { NULL, NULL }
+ };
+
+
+DWORD
+ServiceDispatcherThread (
+ LPVOID Parameter
+ )
+
+/*++
+
+Routine Description:
+
+ This routine synchronizes with the service controller. It waits
+ for the service controller to set the SECURITY_SERVICES_STARTED
+ event then starts up the main
+ thread that is going to handle the control requests from the service
+ controller.
+
+ It basically sets up the ControlDispatcher and, on return, exits from
+ this main thread. The call to NetServiceStartCtrlDispatcher does
+ not return until all services have terminated, and this process can
+ go away.
+
+ It will be up to the ControlDispatcher thread to start/stop/pause/continue
+ any services. If a service is to be started, it will create a thread
+ and then call the main routine of that service.
+
+
+Arguments:
+
+ EventHandle - Event handle to wait on before continuing.
+
+Return Value:
+
+ Exit status of thread.
+
+Note:
+
+
+--*/
+{
+ DWORD WaitStatus;
+ HANDLE EventHandle;
+ BOOL StartStatus;
+
+ //
+ // Create an event for us to wait on.
+ //
+
+ EventHandle = CreateEventW( NULL, // No special security
+ TRUE, // Must be manually reset
+ FALSE, // The event is initially not signalled
+ SECURITY_SERVICES_STARTED );
+
+ if ( EventHandle == NULL ) {
+ WaitStatus = GetLastError();
+
+ //
+ // If the event already exists,
+ // the service controller already created it. Just open it.
+ //
+
+ if ( WaitStatus == ERROR_ALREADY_EXISTS ) {
+
+ EventHandle = OpenEventW( EVENT_ALL_ACCESS,
+ FALSE,
+ SECURITY_SERVICES_STARTED );
+
+ if ( EventHandle == NULL ) {
+ WaitStatus = GetLastError();
+
+ IF_DEBUG() {
+
+ DbgPrint("[Security process] OpenEvent failed %ld\n",
+ WaitStatus );
+ }
+
+ return WaitStatus;
+ }
+
+ } else {
+
+ IF_DEBUG() {
+ DbgPrint("[Security process] CreateEvent failed %ld\n",
+ WaitStatus);
+ }
+
+ return WaitStatus;
+ }
+ }
+
+
+ //
+ // Wait for the service controller to come up.
+ //
+
+ WaitStatus = WaitForSingleObject( (HANDLE) EventHandle, (DWORD) -1 );
+ (VOID) CloseHandle( EventHandle );
+
+ if ( WaitStatus != 0 ) {
+
+ IF_DEBUG() {
+
+ DbgPrint("[Security process] WaitForSingleObject failed %ld\n",
+ WaitStatus );
+ }
+
+ return WaitStatus;
+ }
+
+
+
+ //
+ // Call NetServiceStartCtrlDispatcher to set up the control interface.
+ // The API won't return until all services have been terminated. At that
+ // point, we just exit.
+ //
+
+ StartStatus =
+ StartServiceCtrlDispatcher ( SecurityServiceDispatchTable );
+
+ IF_DEBUG() {
+
+ DbgPrint("[Security process] return from StartCtrlDispatcher %ld \n",
+ StartStatus );
+ }
+
+ return StartStatus;
+
+ UNREFERENCED_PARAMETER(Parameter);
+}
+
+
+NTSTATUS
+ServiceInit (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This is a main routine for the service dispatcher of the security process.
+ It starts up a thread responsible for coordinating with the
+ service controller.
+
+
+Arguments:
+
+ NONE.
+
+Return Value:
+
+ Status of the thread creation operation.
+
+Note:
+
+
+--*/
+{
+ DWORD ThreadId;
+ HANDLE ThreadHandle;
+
+ //
+ // The control dispatcher runs in a thread of its own.
+ //
+
+ ThreadHandle = CreateThread(
+ NULL, // No special thread attributes
+ 0, // No special stack size
+ &ServiceDispatcherThread,
+ NULL, // No special parameter
+ 0, // No special creation flags
+ &ThreadId);
+
+ if ( ThreadHandle == NULL ) {
+ return (NTSTATUS) GetLastError();
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/lsa/server/tadt1.c b/private/lsa/server/tadt1.c
new file mode 100644
index 000000000..31b883d53
--- /dev/null
+++ b/private/lsa/server/tadt1.c
@@ -0,0 +1,454 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ tadt1.c
+
+Abstract:
+
+ Test 1 for auditing.
+
+ This test simply causes the EVENT SOURCE MODULE information
+ read from the registry to be printed out.
+
+
+Author:
+
+ Jim Kelly (JimK) 12-July-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+
+--*/
+
+
+
+
+//
+// THIS DEFINE CAUSES ADTOBJS.C TO PRINT TEST MESSAGES
+//
+
+#define LSAP_ADT_TEST_DUMP_SOURCES 1
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Includes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <nt.h>
+#include <string.h>
+
+#include "lsasrvp.h"
+#include "adtp.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Definitions //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Global variables //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// private macros //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// private service prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+LsapAdtTestAccessesString(
+ VOID
+ );
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+VOID
+_CRTAPI1 main (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main entry routine for this test.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+
+ Note:
+
+
+--*/
+
+
+{
+ NTSTATUS NtStatus;
+
+
+ printf("\n\n\n\n");
+ printf("\t\t\t\t Test: TADT1\n\n");
+ printf("\t\t\t\tTest Date: \n");
+ printf("\t\t\t\tTest Time: \n\n\n");
+
+
+
+
+ //
+ // Call the ADTOBJs initialization routine.
+ //
+
+
+ printf("\tCalling ADTOBJS\\LsapAdtObjsInitialize() Test.\n\n");
+ NtStatus = LsapAdtObjsInitialize();
+
+ printf("\n\n\tADTOBJS\\LsapAdtObjsInitialize() test completed.\n");
+ printf("\tStatus: 0x%lx\n\n", NtStatus);
+
+
+ printf("\n\n\tADTOBJs\\BuildAccessesString() Test.\n\n");
+
+ NtStatus = LsapAdtTestAccessesString();
+
+
+ printf("\n\n\tADTOBJS\\LsapAdtBuildAccessesString() test completed.\n");
+ printf("\tStatus: 0x%lx\n\n", NtStatus);
+
+
+
+
+ return;
+}
+
+
+NTSTATUS
+LsapAdtTestAccessesString(
+ VOID
+ )
+
+{
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+
+ UNICODE_STRING SourceModule,
+ ObjectTypeName,
+ AccessNames;
+
+
+ ACCESS_MASK AccessMask;
+
+
+
+ //
+ // SECURITY source module
+ //
+
+ RtlInitUnicodeString( &SourceModule, L"Security" );
+
+
+ //
+ // Unknown object without accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"NoSuchObject" );
+ AccessMask = 0;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+ //
+ // Unknown object with accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"NoSuchObject" );
+ AccessMask = DELETE | READ_CONTROL | 0x020F;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+ //
+ // Known MS object without accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"File" );
+ AccessMask = 0;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+
+ //
+ // Known MS object with accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"File" );
+ AccessMask = DELETE | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_DATA;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+ //
+ // FRAMITZ_SERVER source module
+ //
+
+ RtlInitUnicodeString( &SourceModule, L"FRAMITZ_server" );
+
+
+ //
+ // Unknown non-MS object without accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"UnknownObjectType" );
+ AccessMask = 0;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+
+ //
+ // Known non-MS object with accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"UnknownObjectType" );
+ AccessMask = ACCESS_SYSTEM_SECURITY | 0x02;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+
+
+
+ //
+ // Known non-MS object without accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"WIDGET" );
+ AccessMask = 0;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+
+ //
+ // Known non-MS object with accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"WIDGET" );
+ AccessMask = ACCESS_SYSTEM_SECURITY | 0x02;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+
+
+
+ //
+ // Unknown source module
+ //
+
+ RtlInitUnicodeString( &SourceModule, L"UnknownSource" );
+
+
+ //
+ // Object without accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"UnknownObjectType" );
+ AccessMask = 0;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+
+ //
+ // Object with accesses
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ RtlInitUnicodeString( &ObjectTypeName, L"UnknownObjectType" );
+ AccessMask = ACCESS_SYSTEM_SECURITY | 0x02;
+ NtStatus = LsapAdtBuildAccessesString( &SourceModule,
+ &ObjectTypeName,
+ AccessMask,
+ &AccessNames
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("\t Returned:\n\t\t%Z\n", &AccessNames);
+ RtlFreeHeap( RtlProcessHeap(), 0, AccessNames.Buffer );
+ }
+ printf("\t Status:\t0x%lx\n\n", NtStatus);
+ }
+
+
+
+
+
+
+
+
+
+
+ return(NtStatus);
+
+
+}
+
+
+
+#include "adtobjs.c"
diff --git a/private/lsa/uclient/crclient.c b/private/lsa/uclient/crclient.c
new file mode 100644
index 000000000..087bf1284
--- /dev/null
+++ b/private/lsa/uclient/crclient.c
@@ -0,0 +1,112 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ crclient.c
+
+Abstract:
+
+ Local Security Authority - Client Cipher Routines
+
+ These routines interface the LSA client side with the Cipher
+ Routines. They perform RPC-style memory allocation.
+
+Author:
+
+ Scott Birrell (ScottBi) December 13, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <lsaclip.h>
+
+
+NTSTATUS
+LsapCrClientGetSessionKey(
+ IN LSA_HANDLE ObjectHandle,
+ OUT PLSAP_CR_CIPHER_KEY *SessionKey
+ )
+
+/*++
+
+Routine Description:
+
+ This function obtains the Session Key, allocates an Cipher Key
+ structure and returns the key.
+
+Arguments:
+
+ ObjectHandle - Handle from an LsaOpen<ObjectType> call.
+
+ SessionKey - Receives a pointer to a structure containing the
+ Session Key in which the memory has been allocated via
+ MIDL_user_allocate().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ (e.g memory) to complete the call.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAP_CR_CIPHER_KEY OutputSessionKey = NULL;
+ ULONG OutputSessionKeyBufferLength;
+
+ //
+ // Allocate memory for the Session Key buffer and LSAP_CR_CIPHER_KEY
+ // structure.
+ //
+
+ OutputSessionKeyBufferLength = sizeof (USER_SESSION_KEY);
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ OutputSessionKey = MIDL_user_allocate(
+ OutputSessionKeyBufferLength +
+ sizeof (LSAP_CR_CIPHER_KEY)
+ );
+
+ if (OutputSessionKey == NULL) {
+
+ goto ClientGetSessionKeyError;
+ }
+
+ //
+ // Fill in the Cipher key structure, making the buffer point to
+ // just beyond the header.
+ //
+
+ OutputSessionKey->Length = OutputSessionKeyBufferLength;
+ OutputSessionKey->MaximumLength = OutputSessionKeyBufferLength;
+ OutputSessionKey->Buffer = (PUCHAR) (OutputSessionKey + 1);
+
+ Status = RtlGetUserSessionKeyClient(
+ ObjectHandle,
+ (PUSER_SESSION_KEY) OutputSessionKey->Buffer
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto ClientGetSessionKeyError;
+ }
+
+
+ClientGetSessionKeyFinish:
+
+ *SessionKey = OutputSessionKey;
+ return(Status);
+
+ClientGetSessionKeyError:
+
+ goto ClientGetSessionKeyFinish;
+}
+
+
diff --git a/private/lsa/uclient/ctlkacct.c b/private/lsa/uclient/ctlkacct.c
new file mode 100644
index 000000000..8d8b95ed1
--- /dev/null
+++ b/private/lsa/uclient/ctlkacct.c
@@ -0,0 +1,378 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ctlkacct.c
+
+Abstract:
+
+ Local Security Authority Subsystem - Short CT for Win 32 LookupAccountName/Sid
+
+ This is a small test that simply calls the Win 32 LookupAccountName/Sid
+ API once.
+
+Usage:
+
+ ctlkacct \\ServerName AccountName
+
+Building Instructions:
+
+ nmake UMTEST=ctlkacc UMTYPE=console
+
+Author:
+
+ Scott Birrell (ScottBi) July 20, 1992
+ [ Adapted from a test written by TimF, not completely to Nt standards ]
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <windows.h>
+
+
+VOID
+DumpSID(
+ IN PSID s
+ );
+
+#define LSA_WIN_STANDARD_BUFFER_SIZE 0x000000200L
+
+VOID _CRTAPI1
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char SidFromLookupName[LSA_WIN_STANDARD_BUFFER_SIZE];
+ char RefDFromLookupName[LSA_WIN_STANDARD_BUFFER_SIZE];
+ DWORD cbSidFromLookupName, cchRefDFromLookupName;
+ SID_NAME_USE UseFromLookupName;
+
+ DWORD cchNameFromLookupSid;
+ char NameFromLookupSid[LSA_WIN_STANDARD_BUFFER_SIZE];
+ char RefDFromLookupSid[LSA_WIN_STANDARD_BUFFER_SIZE];
+ DWORD cchRefDFromLookupSid;
+ SID_NAME_USE UseFromLookupSid;
+ BOOL BoolStatus = TRUE;
+ DWORD LastError;
+
+ if (argc != 3) {
+
+ printf("usage: ctlkacct <(char *)SystemName> <(char *)AccountName>\n");
+ return(0);
+ }
+
+ cbSidFromLookupName = 0;
+ cchRefDFromLookupName = 0;
+
+ BoolStatus = LookupAccountName(argv[1],
+ argv[2],
+ (PSID)SidFromLookupName,
+ &cbSidFromLookupName,
+ RefDFromLookupName,
+ &cchRefDFromLookupName,
+ &UseFromLookupName
+ );
+
+ if (BoolStatus) {
+
+ printf(
+ "LookupAccountName() with zero buffer sizes returned TRUE\n"
+ );
+ }
+
+ //
+ // Get the Last Error (in DOS errorcode from).
+ //
+
+ LastError = GetLastError();
+
+ if (LastError != ERROR_INSUFFICIENT_BUFFER) {
+
+ printf(
+ "Unexpected Last Error %d returned from LookupAccountName\n"
+ "Expected %d (ERROR_INSUFFICIENT_BUFFER)\n",
+ LastError
+ );
+ }
+
+ //
+ // Now call LookupAccountName() again, using the buffer sizes returned.
+ //
+
+ BoolStatus = LookupAccountName(argv[1],
+ argv[2],
+ (PSID)SidFromLookupName,
+ &cbSidFromLookupName,
+ RefDFromLookupName,
+ &cchRefDFromLookupName,
+ &UseFromLookupName
+ );
+
+ if (!BoolStatus) {
+
+ printf(
+ "LookupAccountName failed - \n"
+ "LastError = %d\n",
+ GetLastError()
+ );
+
+ return(0);
+ }
+
+ /*
+ * Print information returned by LookupAccountName
+ */
+
+ printf(
+ "*********************************************\n"
+ "Information returned by LookupAccountName\n"
+ "*********************************************\n\n"
+ );
+
+ printf( "Sid = " );
+ DumpSID((PSID) SidFromLookupName);
+ printf(
+ "Size of Sid = %d\n",
+ cbSidFromLookupName
+ );
+
+ printf(
+ "Referenced Domain Name = %s\n"
+ "Size of Referenced Domain Name = %d\n",
+ RefDFromLookupName,
+ cchRefDFromLookupName
+ );
+
+ printf("Name Use = ");
+
+ switch (UseFromLookupName) {
+
+ case SidTypeUser:
+
+ printf("SidTypeUser\n");
+ break;
+
+ case SidTypeGroup:
+
+ printf("SidTypeGroup\n");
+ break;
+
+ case SidTypeDomain:
+
+ printf("SidTypeDomain\n");
+ break;
+
+ case SidTypeAlias:
+
+ printf("SidTypeAlias\n");
+ break;
+
+ case SidTypeWellKnownGroup:
+
+ printf("SidTypeWellKnownGroup\n");
+ break;
+
+ case SidTypeDeletedAccount:
+
+ printf("SidTypeDeletedAccount\n");
+ break;
+
+ case SidTypeInvalid:
+
+ printf("SidTypeInvalid\n");
+ break;
+
+ case SidTypeUnknown:
+
+ printf("SidTypeUnknown\n");
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ cchNameFromLookupSid = 0;
+ cchRefDFromLookupSid = 0;
+
+ //
+ // Now lookup the Sid we just obtained and see if we get the name back.
+ // First, provide zero buffer sizes so that we get the sizes needed
+ // returned.
+ //
+
+ cchNameFromLookupSid = 0;
+ cchRefDFromLookupSid = 0;
+
+ BoolStatus = LookupAccountSid(argv[1],
+ (PSID) SidFromLookupName,
+ NameFromLookupSid,
+ &cchNameFromLookupSid,
+ RefDFromLookupSid,
+ &cchRefDFromLookupSid,
+ &UseFromLookupSid
+ );
+
+ if (BoolStatus) {
+
+ printf("LookupAccountSid() with zero buffer sizes returned TRUE\n");
+ }
+
+ //
+ // Get the Last Error (in DOS errorcode from).
+ //
+
+ LastError = GetLastError();
+
+ if (LastError != ERROR_INSUFFICIENT_BUFFER) {
+
+ printf(
+ "Unexpected Last Error %d returned from LookupAccountSid\n"
+ "Expected %d (ERROR_INSUFFICIENT_BUFFER)\n",
+ LastError
+ );
+ }
+
+ //
+ // Now call LookupAccountSid() again, using the buffer sizes obtained
+ // from the previous call.
+ //
+
+ if (!LookupAccountSid(argv[1],
+ (PSID) SidFromLookupName,
+ NameFromLookupSid,
+ &cchNameFromLookupSid,
+ RefDFromLookupSid,
+ &cchRefDFromLookupSid,
+ &UseFromLookupSid
+ )) {
+
+ printf(
+ "LookupAccountSid failed\n"
+ "LastError = %d\n",
+ GetLastError()
+ );
+
+ return(0);
+ }
+
+ /*
+ * Print information returned by LookupAccountSid
+ */
+
+ printf(
+ "*********************************************\n"
+ "Information returned by LookupAccountSid\n"
+ "*********************************************\n\n"
+ );
+
+ printf(
+ "Account Name = %s\n"
+ "Account Name Size (chars) = %d\n"
+ "Referenced Domain Name = %s\n"
+ "Referenced Domain Size (chars) = %d\n",
+ NameFromLookupSid,
+ cchNameFromLookupSid,
+ RefDFromLookupSid,
+ cchRefDFromLookupSid
+ );
+
+ printf("Sid Use = ");
+
+ switch (UseFromLookupSid) {
+
+ case SidTypeUser:
+
+ printf("SidTypeUser\n");
+ break;
+
+ case SidTypeGroup:
+
+ printf("SidTypeGroup\n");
+ break;
+
+ case SidTypeDomain:
+
+ printf("SidTypeDomain\n");
+ break;
+
+ case SidTypeAlias:
+
+ printf("SidTypeAlias\n");
+ break;
+
+ case SidTypeWellKnownGroup:
+
+ printf("SidTypeWellKnownGroup\n");
+ break;
+
+ case SidTypeDeletedAccount:
+
+ printf("SidTypeDeletedAccount\n");
+ break;
+
+ case SidTypeInvalid:
+
+ printf("SidTypeInvalid\n");
+ break;
+
+ case SidTypeUnknown:
+
+ printf("SidTypeUnknown\n");
+ break;
+
+ default:
+
+ break;
+ }
+
+ return(0);
+}
+
+
+VOID
+DumpSID(
+ IN PSID s
+ )
+
+{
+ static char b[128];
+
+ SID_IDENTIFIER_AUTHORITY *a;
+ ULONG id = 0, i;
+
+ try {
+
+ b[0] = '\0';
+
+ a = GetSidIdentifierAuthority(s);
+
+ sprintf(b, "s-0x1-%02x%02x%02x%02x%02x%02x", a -> Value[0],
+ a -> Value[1], a -> Value[2], a -> Value[3], a ->
+ Value[4], a -> Value[5]);
+
+ for (i = 0; i < *GetSidSubAuthorityCount(s); i++) {
+
+ sprintf(b, "%s-0x%lx", b, *GetSidSubAuthority(s, i));
+ }
+
+ printf("%s\n", b);
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ if (*b) {
+
+ printf("%s", b);
+ }
+
+ printf("<invalid pointer (0x%lx)>\n", s);
+ }
+}
+
diff --git a/private/lsa/uclient/ctlklsa.c b/private/lsa/uclient/ctlklsa.c
new file mode 100644
index 000000000..fc6b79d50
--- /dev/null
+++ b/private/lsa/uclient/ctlklsa.c
@@ -0,0 +1,579 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ctlklsa.c
+
+Abstract:
+
+ Local Security Authority Subsystem - Short CT for Lsa LookupAccountName/Sid
+
+ This is a small test that simply calls the LsaLookupName/Sid API once.
+
+Usage:
+
+ ctlklsa \\ServerName [-p] -a AccountName ... AccountName
+
+ -p - pause before name and sid lookup operation
+ -a - start of list of account names
+
+
+Building Instructions:
+
+ nmake UMTEST=ctlklsa UMTYPE=console
+
+Author:
+
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsaclip.h"
+
+
+VOID
+DumpSID(
+ IN PSID s
+ );
+
+VOID
+CtPause(
+ );
+
+#define LSA_WIN_STANDARD_BUFFER_SIZE 0x000000200L
+
+VOID _CRTAPI1
+main(argc, argv)
+int argc;
+char **argv;
+{
+ NTSTATUS
+ Status = STATUS_SUCCESS,
+ SidStatus;
+
+ ULONG
+ Index,
+ ArgCount = (ULONG) argc,
+ DomainIndex,
+ NameIndex,
+ NameCount,
+ DomainSidLength;
+
+ ANSI_STRING
+ NamesAnsi[ LSA_WIN_STANDARD_BUFFER_SIZE],
+ SystemNameAnsi;
+
+ UNICODE_STRING
+ Names[ LSA_WIN_STANDARD_BUFFER_SIZE],
+ SystemName;
+
+ PUNICODE_STRING
+ DomainName;
+
+ PSID
+ Sid = NULL,
+ *Sids = NULL;
+
+ SECURITY_QUALITY_OF_SERVICE
+ SecurityQualityOfService;
+
+ OBJECT_ATTRIBUTES
+ ObjectAttributes;
+
+ LSA_HANDLE
+ PolicyHandle;
+
+ PLSA_REFERENCED_DOMAIN_LIST
+ ReferencedDomains = NULL,
+ ReferencedSidsDomains = NULL;
+
+ PLSA_TRANSLATED_SID
+ TranslatedSids = NULL;
+
+ PLSA_TRANSLATED_NAME
+ TranslatedNames = NULL;
+
+ UCHAR
+ SubAuthorityCount;
+
+ BOOLEAN
+ Pause = FALSE,
+ DoLookupSids = TRUE;
+
+ if (argc < 4) {
+
+ printf("usage: ctlkacct <ServerName> [-p] -a <AccountName> [<AccountName> ...]\n\n");
+ printf(" -p - pause before name and sid lookup operations\n");
+ printf(" -a - start of list of account names\n\n");
+ printf("example:\n");
+ printf(" ctlklsa \\\\jimk -p -a interactive \"domain guests\" administrators\n\n");
+ return;
+ }
+
+ NameIndex = 0;
+
+ //
+ // Capture argument 1, the Server Name
+ //
+
+ RtlInitString( &SystemNameAnsi, (PUCHAR) argv[1] );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &SystemName,
+ &SystemNameAnsi,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MainError;
+ }
+
+ for (Index = 2; Index < ArgCount; Index++) {
+
+ if (strncmp(argv[Index], "-p", 2) == 0) {
+
+ //
+ // The caller wants a pause before each lookup call.
+ //
+
+ Pause = TRUE;
+ } else if (strncmp(argv[Index], "-a", 2) == 0) {
+
+ Index++;
+
+ while (Index < ArgCount) {
+
+ if (strncmp(argv[Index], "-", 1) == 0) {
+
+ Index--;
+ break;
+ }
+
+ //
+ // Capture the Account Name as a Unicode String.
+ //
+
+ RtlInitString( &NamesAnsi[ NameIndex ], argv[Index] );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &Names[ NameIndex ],
+ &NamesAnsi[ NameIndex ],
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ break;
+ }
+
+ NameIndex++;
+ Index++;
+ }
+
+ if (Index == ArgCount) {
+
+ break;
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MainError;
+ }
+
+ NameCount = NameIndex;
+
+ SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
+ SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQualityOfService.EffectiveOnly = FALSE;
+
+ //
+ // Set up the object attributes prior to opening the LSA.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL
+ );
+
+ //
+ // The InitializeObjectAttributes macro presently stores NULL for
+ // the SecurityQualityOfService field, so we must manually copy that
+ // structure for now.
+ //
+
+ ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
+
+ //
+ // Open the LSA Policy Database for the target system. This is the
+ // starting point for the Name Lookup operation.
+ //
+
+ Status = LsaOpenPolicy(
+ &SystemName,
+ &ObjectAttributes,
+ POLICY_LOOKUP_NAMES,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MainError;
+ }
+
+ if (Pause) {
+
+ printf( "\n\n..... Pausing before name lookup \n ");
+ CtPause( );
+ }
+
+
+ //
+ // Attempt to translate the Names to Sids.
+ //
+
+ Status = LsaLookupNames(
+ PolicyHandle,
+ NameCount,
+ Names,
+ &ReferencedDomains,
+ &TranslatedSids
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto MainError;
+ }
+
+ //
+ // Build the Sids from the output.
+ //
+
+ Sids = (PSID *) LocalAlloc( 0, NameCount * sizeof (PSID) );
+
+
+ if (Sids == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto MainError;
+ }
+
+
+ for (NameIndex = 0; NameIndex < NameCount; NameIndex++) {
+
+ if (TranslatedSids[NameIndex].Use == SidTypeUnknown) {
+
+ Sids[NameIndex] = NULL;
+ DoLookupSids = FALSE;
+
+ } else {
+ DomainIndex = TranslatedSids[NameIndex].DomainIndex;
+
+ DomainSidLength = RtlLengthSid(
+ ReferencedDomains->Domains[DomainIndex].Sid
+ );
+
+
+
+ Sid = (PSID) LocalAlloc( (UINT) 0, (UINT) (DomainSidLength + sizeof(ULONG)) );
+
+ if (Sid == NULL) {
+ printf(" ** ERROR - couldn't allocate heap !!\n");
+ return;
+ }
+
+ RtlMoveMemory(
+ Sid,
+ ReferencedDomains->Domains[DomainIndex].Sid,
+ DomainSidLength
+ );
+
+ if (TranslatedSids[NameIndex].Use != SidTypeDomain) {
+
+ (*RtlSubAuthorityCountSid( Sid ))++;
+ SubAuthorityCount = *RtlSubAuthorityCountSid( Sid );
+ *RtlSubAuthoritySid(Sid,SubAuthorityCount - (UCHAR) 1) =
+ TranslatedSids[NameIndex].RelativeId;
+ }
+
+ Sids[NameIndex] = Sid;
+ }
+ }
+
+
+ //
+ // Pause before SID lookup...
+ //
+
+ if (!DoLookupSids) {
+ printf("\n\n Unknown Name causing sid lookup to be skipped\n");
+ } else {
+ if (Pause) {
+
+ printf( "\n\n..... Pausing before SID lookup \n ");
+ CtPause();
+ }
+
+ //
+ // Now translate the Sids back to Names
+ //
+
+ SidStatus = LsaLookupSids(
+ PolicyHandle,
+ NameCount,
+ (PSID *) Sids,
+ &ReferencedSidsDomains,
+ &TranslatedNames
+ );
+ }
+
+ /*
+ * Print information returned by LookupAccountName
+ */
+
+
+ printf(
+ "*********************************************\n"
+ "Information returned by LookupAccountName\n"
+ "*********************************************\n\n"
+ );
+
+ for (NameIndex = 0; NameIndex < NameCount; NameIndex++) {
+
+
+ printf(" Name looked up: *%wZ*\n", &Names[NameIndex]);
+
+ printf(" Use = ");
+
+ switch (TranslatedSids[NameIndex].Use) {
+
+ case SidTypeUser:
+
+ printf("SidTypeUser\n");
+ break;
+
+ case SidTypeGroup:
+
+ printf("SidTypeGroup\n");
+ break;
+
+ case SidTypeDomain:
+
+ printf("SidTypeDomain\n");
+ break;
+
+ case SidTypeAlias:
+
+ printf("SidTypeAlias\n");
+ break;
+
+ case SidTypeWellKnownGroup:
+
+ printf("SidTypeWellKnownGroup\n");
+ break;
+
+ case SidTypeDeletedAccount:
+
+ printf("SidTypeDeletedAccount\n");
+ break;
+
+ case SidTypeInvalid:
+
+ printf("SidTypeInvalid\n");
+ break;
+
+ case SidTypeUnknown:
+
+ printf("SidTypeUnknown\n\n");
+ break;
+
+ default:
+
+ printf("Hmmm - something unusual came back !!!! \n\n");
+ break;
+
+ }
+
+ if (TranslatedSids[NameIndex].Use != SidTypeUnknown) {
+
+ printf(" Sid = " );
+ DumpSID((PSID) Sids[NameIndex]);
+
+ DomainIndex = TranslatedSids[NameIndex].DomainIndex;
+ DomainName = &ReferencedDomains->Domains[ DomainIndex ].Name;
+
+ printf(" Referenced Domain Name = *%wZ*\n\n", DomainName );
+ }
+ }
+
+
+ if (DoLookupSids) {
+ printf(
+ "*********************************************\n"
+ "Information returned by LookupAccountSid\n"
+ "*********************************************\n\n"
+ );
+
+ if (!NT_SUCCESS(SidStatus)) {
+ printf(" Sid lookup failed. Status 0x%lx\n"
+ " Dumping sids that were being looked up...\n",
+ SidStatus);
+ }
+
+ for (NameIndex = 0; NameIndex < NameCount; NameIndex++) {
+
+
+ printf(" Sid = " );
+ DumpSID((PSID) Sids[NameIndex]);
+
+ if (NT_SUCCESS(SidStatus)) {
+
+ printf(" Sid Use = ");
+
+ switch (TranslatedNames[NameIndex].Use) {
+
+ case SidTypeUser:
+
+ printf("SidTypeUser\n");
+ break;
+
+ case SidTypeGroup:
+
+ printf("SidTypeGroup\n");
+ break;
+
+ case SidTypeDomain:
+
+ printf("SidTypeDomain\n");
+ break;
+
+ case SidTypeAlias:
+
+ printf("SidTypeAlias\n");
+ break;
+
+ case SidTypeWellKnownGroup:
+
+ printf("SidTypeWellKnownGroup\n");
+ break;
+
+ case SidTypeDeletedAccount:
+
+ printf("SidTypeDeletedAccount\n");
+ break;
+
+ case SidTypeInvalid:
+
+ printf("SidTypeInvalid\n");
+ break;
+
+ case SidTypeUnknown:
+
+ printf("SidTypeUnknown\n");
+ break;
+
+ default:
+
+ printf("Hmmm - unexpected value !!!\n");
+ break;
+ }
+
+ DomainIndex = TranslatedNames[NameIndex].DomainIndex;
+ DomainName = &ReferencedSidsDomains->Domains[ DomainIndex ].Name;
+ if (TranslatedNames[NameIndex].Use == SidTypeDomain) {
+ printf(
+ " Domain Name = *%wZ*\n\n",
+ DomainName
+ );
+ } else {
+ printf(
+ " Account Name = *%wZ*\n"
+ " Referenced Domain Name = *%wZ*\n\n",
+ &TranslatedNames[NameIndex].Name,
+ DomainName
+ );
+ }
+ }
+ }
+ }
+
+
+MainFinish:
+
+ return;
+
+MainError:
+
+ printf("Error: status = 0x%lx\n", Status);
+
+ goto MainFinish;
+}
+
+
+VOID
+DumpSID(
+ IN PSID s
+ )
+
+{
+ SID_IDENTIFIER_AUTHORITY
+ *a;
+
+ ULONG
+ id = 0,
+ i;
+
+ BOOLEAN
+ PrintValue = FALSE;
+
+ try {
+
+
+ a = GetSidIdentifierAuthority(s);
+
+ printf("s-1-");
+
+ for (i=0; i<5; i++) {
+ if ((a->Value[i] != 0) || PrintValue) {
+ printf("%02x", a->Value[i]);
+ PrintValue = TRUE;
+ }
+ }
+ printf("%02x", a->Value[5]);
+
+
+ for (i = 0; i < (ULONG) *GetSidSubAuthorityCount(s); i++) {
+
+ printf("-0x%lx", *GetSidSubAuthority(s, i));
+ }
+
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ printf("<invalid pointer (0x%lx)>\n", s);
+ }
+ printf("\n");
+}
+
+VOID
+CtPause(
+ )
+{
+ CHAR
+ IgnoreInput[300];
+
+ printf("Press <ENTER> to continue . . .");
+ gets( &IgnoreInput[0] );
+ return;
+}
diff --git a/private/lsa/uclient/ctreg.c b/private/lsa/uclient/ctreg.c
new file mode 100644
index 000000000..edf6a5acb
--- /dev/null
+++ b/private/lsa/uclient/ctreg.c
@@ -0,0 +1,807 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ctreg.c
+
+Abstract:
+
+ Configuration Registry component test
+
+ Needs to move from here
+
+Author:
+
+ Scott Birrell (ScottBi) June 5, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+
+
+#define CT_REG_INITIAL_KEY_COUNT 8L
+#define CT_REG_INITIAL_LEVEL_COUNT 4L
+#define CT_REG_KEY_VALUE_MAX_LENGTH 0x00000100L
+
+//
+// List of initial Registry keys to be set up. The list must be
+// kept so that moving linearly through it visits key nodes with top
+// to bottom key traversal taking precedence over left-to-right.
+//
+
+typedef struct _CT_TEST_REGISTRY_KEY {
+ ULONG KeyLevel;
+ PUCHAR KeyName;
+ ULONG KeyValueType;
+ PUCHAR KeyValue;
+ ULONG KeyValueLengthToQuery;
+ ULONG KeyValueLengthToSet;
+ NTSTATUS ExpectedStatus;
+ HANDLE KeyHandle;
+ HANDLE ParentKeyHandle;
+} CT_TEST_REGISTRY_KEY, *PCT_TEST_REGISTRY_KEY;
+
+CT_TEST_REGISTRY_KEY RegistryKeys[ CT_REG_INITIAL_KEY_COUNT ];
+
+UCHAR KeyValue[CT_REG_KEY_VALUE_MAX_LENGTH];
+ULONG KeyValueLengthToQuery;
+ULONG KeyValueType;
+LARGE_INTEGER LastWriteTime;
+
+HANDLE ParentKeyHandle[CT_REG_INITIAL_LEVEL_COUNT + 1] =
+ { NULL, NULL, NULL, NULL, NULL };
+
+VOID
+InitTestKey(
+ IN ULONG KeyNumber,
+ IN ULONG KeyLevel,
+ IN PUCHAR KeyName,
+ IN ULONG KeyNameLength,
+ IN ULONG KeyValueType,
+ IN PUCHAR KeyValue,
+ IN ULONG KeyValueLengthToQuery,
+ IN ULONG KeyValueLengthToSet,
+ IN NTSTATUS ExpectedStatus
+ );
+
+
+VOID
+CtRegExamineResult(
+ IN ULONG KeyNumber,
+ IN ULONG KeyValueType,
+ IN PUCHAR KeyValue,
+ IN ULONG KeyValueLength,
+ IN NTSTATUS ReturnedStatus
+ );
+
+
+VOID
+CtCreateSetQueryKey();
+
+VOID
+CtOpenMakeTempCloseKey();
+
+
+VOID
+main ()
+{
+ CtCreateSetQueryKey();
+
+ CtOpenMakeTempCloseKey();
+
+}
+
+
+
+
+VOID
+CtCreateSetQueryKey(
+ )
+
+/*++
+
+Routine Description:
+
+ This function tests the RtlpNtOpenKey RtlpNtCreateKey and NtClose API.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Set up a test hierarchy of keys
+ //
+
+ ULONG KeyNumber;
+ ULONG ZeroLength;
+ STRING Name;
+ UNICODE_STRING UnicodeName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ ZeroLength = 0L;
+
+ DbgPrint("Start of Create, Set, Query Registry Key Test\n");
+
+ //
+ // Initialize the test array. I did it this way because a statically
+ // initialized table is harder to read.
+ //
+
+ InitTestKey(
+ 0,
+ 0,
+ "\\Registry\\RLM",
+ sizeof ("\\Registry\\RLM"),
+ 0,
+ "Level 0 - RLM",
+ sizeof ("\\Registry\\RLM") - 1,
+ sizeof ("\\Registry\\RLM") - 1,
+ STATUS_SUCCESS
+ );
+
+ InitTestKey(
+ 1,
+ 1,
+ "Test",
+ sizeof ("Test"),
+ 1,
+ "Keyname Test - this value too big",
+ 6,
+ sizeof ("Keyname Test - this value too big") -1,
+ STATUS_BUFFER_OVERFLOW
+ );
+
+ InitTestKey(
+ 2,
+ 2,
+ "SubTestA",
+ sizeof("SubTestA"),
+ 2,
+ "Keyname SubTestA - value small",
+ 30,
+ sizeof ("Keyname SubTestA - value small") - 1,
+ STATUS_SUCCESS
+ );
+
+
+ InitTestKey(
+ 3,
+ 3,
+ "SSTestA",
+ sizeof("SSTestA"),
+ 3,
+ "Keyname SSTestA - zero length buffer",
+ 30,
+ sizeof("Keyname SSTestA - zero length buffer") - 1,
+ STATUS_BUFFER_OVERFLOW
+ );
+
+ InitTestKey(
+ 4,
+ 3,
+ "SSTestB",
+ sizeof("SSTestB"),
+ 4,
+ "Keyname SSTestB - value exact fit",
+ sizeof ("Keyname SSTestB - value exact fit") -1,
+ sizeof ("Keyname SSTestB - value exact fit") -1,
+ STATUS_SUCCESS
+ );
+
+ InitTestKey(
+ 5,
+ 3,
+ "SSTestC",
+ sizeof("SSTestC"),
+ 5,
+ "Keyname SSTestC - value small",
+ 40,
+ sizeof ("Keyname SSTestC - value small") -1,
+ STATUS_SUCCESS
+ );
+
+ InitTestKey(
+ 6,
+ 3,
+ "SSTestD",
+ sizeof("SSTestD"),
+ 6,
+ "Keyname SSTestD - value small",
+ 40,
+ sizeof ("Keyname SSTestD - value small") -1,
+ STATUS_SUCCESS
+ );
+
+ InitTestKey(
+ 7,
+ 3,
+ "SSTestE",
+ sizeof("SSTestD"),
+ 0,
+ "Keyname SSTestD - no value set",
+ 40,
+ 0,
+ STATUS_SUCCESS
+ );
+
+ DbgPrint("Start of Registry Test\n");
+
+ //
+ // Create all of the initial test registry keys
+ //
+
+
+ for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {
+
+ RtlInitString(
+ &Name,
+ RegistryKeys[KeyNumber].KeyName
+ );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeName,
+ &Name,
+ TRUE );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Security: Registry Init Ansi to Unicode failed 0x%lx\n",
+ Status);
+ return;
+ }
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeName,
+ OBJ_CASE_INSENSITIVE,
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel],
+ NULL
+ );
+
+ //
+ // Remember the Parent Key handle
+ //
+
+ RegistryKeys[KeyNumber].ParentKeyHandle =
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel];
+
+ //
+ // Create the key if it does not already exist. If key does exist,
+ // continue trying to create all of the other keys needed (for now).
+ // Store the returned key handle as the parent key handle for the
+ // next higher (child) level.
+ //
+
+ Status = RtlpNtCreateKey(
+ &RegistryKeys[KeyNumber].KeyHandle,
+ (KEY_READ | KEY_WRITE),
+ &ObjectAttributes,
+ 0L, // No options
+ NULL, // Default provider
+ NULL
+ );
+
+ //
+ // Save the Key's handle as the next level's parent handle.
+ //
+
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1] =
+ RegistryKeys[KeyNumber].KeyHandle;
+
+ //
+ // Free the memory allocated for the Unicode name
+ //
+
+ RtlFreeUnicodeString( &UnicodeName );
+
+ if (NT_SUCCESS(Status) || Status == STATUS_OBJECT_NAME_COLLISION) {
+
+ //
+ // Set the key's value unless the length to set is zero.
+ //
+
+ if (RegistryKeys[KeyNumber].KeyValueLengthToSet != 0) {
+
+ Status=RtlpNtSetValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel+1],
+ RegistryKeys[KeyNumber].KeyValueType,
+ RegistryKeys[KeyNumber].KeyValue,
+ RegistryKeys[KeyNumber].KeyValueLengthToSet
+ );
+ }
+
+ //
+ // Read the key's value back
+ //
+
+ KeyValueLengthToQuery =
+ RegistryKeys[KeyNumber].KeyValueLengthToQuery;
+
+ Status = RtlpNtQueryValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
+ &KeyValueType,
+ KeyValue,
+ &KeyValueLengthToQuery,
+ &LastWriteTime
+ );
+
+ //
+ // Verify that the expected KeyValue, KeyLength and Status
+ // were returned.
+ //
+
+ CtRegExamineResult(
+ KeyNumber,
+ KeyValueType,
+ KeyValue,
+ KeyValueLengthToQuery,
+ Status
+ );
+
+ //
+ // Test null pointer cases don't crash NtQueryValueKey
+ //
+
+ Status = RtlpNtQueryValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
+ NULL, // No type
+ KeyValue,
+ &KeyValueLengthToQuery,
+ &LastWriteTime
+ );
+
+ Status = RtlpNtQueryValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
+ &KeyValueType,
+ NULL, // No value
+ &KeyValueLengthToQuery,
+ &LastWriteTime
+ );
+
+ Status = RtlpNtQueryValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
+ &KeyValueType,
+ KeyValue,
+ NULL, // No length
+ &LastWriteTime
+ );
+
+ Status = RtlpNtQueryValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
+ &KeyValueType,
+ KeyValue,
+ &ZeroLength, // Zero length
+ &LastWriteTime
+ );
+
+ Status = RtlpNtQueryValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
+ &KeyValueType,
+ KeyValue,
+ &KeyValueLengthToQuery,
+ NULL // No time
+ );
+ //
+ // Test null pointer cases don't crash NtSetValueKey
+ //
+
+ Status = RtlpNtSetValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel+1],
+ RegistryKeys[KeyNumber].KeyValueType,
+ NULL, // No value, setting type only
+ RegistryKeys[KeyNumber].KeyValueLengthToSet
+ );
+ } else {
+
+ DbgPrint(
+ "Key number %d creation failed 0x%lx\n",
+ KeyNumber,
+ Status
+ );
+ }
+ }
+
+ //
+ // Close all the keys in the table
+ //
+
+ for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {
+
+ Status = NtClose(
+ RegistryKeys[KeyNumber].KeyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("Closing KeyNumber %d failed 0x%lx\n", Status);
+ }
+ }
+
+ DbgPrint("End of Create, Set, Query Registry Key Test\n");
+}
+
+
+VOID
+CtRegExamineResult(
+ IN ULONG KeyNumber,
+ IN ULONG KeyValueType,
+ IN PUCHAR KeyValue,
+ IN ULONG KeyValueLengthReturned,
+ IN NTSTATUS ReturnedStatus
+ )
+
+{
+ ULONG KeyValueLengthToCompare;
+
+ //
+ // Check the status. If bad, skip the other checks.
+ //
+
+ if (ReturnedStatus != RegistryKeys[KeyNumber].ExpectedStatus) {
+
+ DbgPrint(
+ "KeyNumber %d: RtlpNtQueryValueKey returned 0x%lx, expected 0x%lx\n",
+ KeyNumber,
+ ReturnedStatus,
+ RegistryKeys[KeyNumber].ExpectedStatus
+ );
+
+ } else {
+
+ //
+ // Check that the Key Type is as expected.
+ //
+
+
+ if (KeyValueType != RegistryKeys[KeyNumber].KeyValueType) {
+
+ DbgPrint(
+ "KeyNumber %d: RtlpNtQueryValueKey returned KeyValueType 0x%lx, \
+ expected 0x%lx\n",
+ KeyNumber,
+ KeyValueType,
+ RegistryKeys[KeyNumber].KeyValueType
+ );
+
+ }
+
+ //
+ // Check that the Key Length is as expected.
+ //
+
+
+ if (KeyValueLengthReturned !=
+ RegistryKeys[KeyNumber].KeyValueLengthToSet) {
+
+ DbgPrint(
+ "KeyNumber %d: RtlpNtQueryValueKey returned ValLength 0x%lx, \
+ expected 0x%lx\n",
+ KeyNumber,
+ KeyValueLengthReturned,
+ RegistryKeys[KeyNumber].KeyValueLengthToSet
+ );
+
+ }
+
+ //
+ // Check that the Key Value is as expected. Distinguish between
+ // Buffer truncated cases and regular cases.
+ //
+
+ if (RegistryKeys[KeyNumber].KeyValueLengthToSet != 0L) {
+
+ //
+ // Determine the length of returned key value to compare. This is
+ // the min of the set length and and the size of the return
+ // buffer.
+ //
+
+ KeyValueLengthToCompare =
+ RegistryKeys[KeyNumber].KeyValueLengthToSet;
+
+ if (KeyValueLengthToCompare >
+ RegistryKeys[KeyNumber].KeyValueLengthToQuery) {
+
+ KeyValueLengthToCompare =
+ RegistryKeys[KeyNumber].KeyValueLengthToQuery;
+ }
+
+
+ if (strncmp(
+ KeyValue,
+ RegistryKeys[KeyNumber].KeyValue,
+ KeyValueLengthToCompare
+ ) != 0) {
+
+ //
+ // Output approriate error message. Message contains
+ // "truncated.." if key value should have been truncated
+ //
+
+ if (RegistryKeys[KeyNumber].KeyValueLengthToSet >
+ RegistryKeys[KeyNumber].KeyValueLengthToQuery) {
+
+ DbgPrint(
+ "KeyNumber %d: RtlpNtQueryValueKey returned KeyValue %s, \
+ expected %s truncated to %d characters\n",
+ KeyNumber,
+ KeyValue,
+ RegistryKeys[KeyNumber].KeyValue,
+ RegistryKeys[KeyNumber].KeyValueLengthToQuery
+ );
+
+ } else {
+
+ DbgPrint(
+ "KeyNumber %d: RtlpNtQueryValueKey returned KeyValue %s, \
+ expected %s\n",
+ KeyNumber,
+ KeyValue,
+ RegistryKeys[KeyNumber].KeyValue
+ );
+ }
+ }
+ }
+ }
+}
+
+
+
+
+VOID
+CtOpenMakeTempCloseKey()
+
+/*++
+
+Routine Description:
+
+ This function tests NtDeleteKey by deleting the CT configuration
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG KeyNumber;
+ ULONG KeyLevel;
+ STRING Name;
+ UNICODE_STRING UnicodeName;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ //
+ // Open all of the initial test registry keys for write and delete
+ // access, set, query and delete each key.
+ //
+
+ DbgPrint("Start of Open Make Temp and Close Delete Registry Key Test\n");
+
+ //
+ // First, set all of the Parent handles to NULL
+ //
+
+ for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {
+
+ RegistryKeys[KeyNumber].ParentKeyHandle = NULL;
+ }
+
+ for (KeyLevel = 0; KeyLevel < CT_REG_INITIAL_LEVEL_COUNT; KeyLevel++) {
+
+ ParentKeyHandle[KeyLevel] = NULL;
+ }
+
+ for (KeyNumber = 0; KeyNumber < CT_REG_INITIAL_KEY_COUNT; KeyNumber++) {
+
+ RtlInitString(
+ &Name,
+ RegistryKeys[KeyNumber].KeyName
+ );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeName,
+ &Name,
+ TRUE );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("Security: Registry Init Ansi to Unicode failed 0x%lx\n",
+ Status);
+ return;
+ }
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeName,
+ OBJ_CASE_INSENSITIVE,
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel],
+ NULL
+ );
+
+ //
+ // Open the key and store the returned key handle as the parent key
+ // handle for the next higher (child) level.
+ //
+
+ Status = RtlpNtOpenKey(
+ &RegistryKeys[KeyNumber].KeyHandle,
+ (KEY_READ | KEY_WRITE | DELETE),
+ &ObjectAttributes,
+ 0L // No options
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "NtOpenKey - KeyNumber %d failed 0x%lx\n",
+ KeyNumber,
+ Status
+ );
+ }
+
+ //
+ // Save the Key's handle as the next level's parent handle.
+ //
+
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1] =
+ RegistryKeys[KeyNumber].KeyHandle;
+
+ //
+ // Free the memory allocated for the Unicode name
+ //
+
+ RtlFreeUnicodeString( &UnicodeName );
+
+ if (NT_SUCCESS(Status) || Status == STATUS_OBJECT_NAME_COLLISION) {
+
+ //
+ // Set the key's value unless the length to set is zero.
+ //
+
+ if (RegistryKeys[KeyNumber].KeyValueLengthToSet != 0) {
+
+ Status=RtlpNtSetValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel+1],
+ RegistryKeys[KeyNumber].KeyValueType,
+ RegistryKeys[KeyNumber].KeyValue,
+ RegistryKeys[KeyNumber].KeyValueLengthToSet
+ );
+ }
+
+ //
+ // Read the key's value back
+ //
+
+ KeyValueLengthToQuery =
+ RegistryKeys[KeyNumber].KeyValueLengthToQuery;
+
+ Status = RtlpNtQueryValueKey(
+ ParentKeyHandle[RegistryKeys[KeyNumber].KeyLevel + 1],
+ &KeyValueType,
+ KeyValue,
+ &KeyValueLengthToQuery,
+ &LastWriteTime
+ );
+
+ //
+ // Verify that the expected KeyValue, KeyLength and Status
+ // were returned.
+ //
+
+ CtRegExamineResult(
+ KeyNumber,
+ KeyValueType,
+ KeyValue,
+ KeyValueLengthToQuery,
+ Status
+ );
+
+ } else {
+
+ DbgPrint(
+ "Key number %d open failed 0x%lx\n",
+ KeyNumber,
+ Status
+ );
+ }
+ }
+
+ //
+ // Make Temporary and Close all the keys in the table
+ //
+
+ for (KeyNumber = CT_REG_INITIAL_KEY_COUNT-1; KeyNumber != 0L; KeyNumber--) {
+
+ Status = RtlpNtMakeTemporaryKey( RegistryKeys[KeyNumber].KeyHandle );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "Making Temporary KeyNumber %d failed 0x%lx\n",
+ KeyNumber,
+ Status
+ );
+ }
+
+ Status = NtClose(
+ RegistryKeys[KeyNumber].KeyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint(
+ "Closing KeyNumber %d failed 0x%lx\n",
+ KeyNumber,
+ Status
+ );
+ }
+ }
+
+ DbgPrint("End of Open Mk Temp and Close Registry Key Test\n");
+}
+
+
+VOID
+InitTestKey(
+ IN ULONG KeyNumber,
+ IN ULONG KeyLevel,
+ IN PUCHAR KeyName,
+ IN ULONG KeyNameLength,
+ IN ULONG KeyValueType,
+ IN PUCHAR KeyValue,
+ IN ULONG KeyValueLengthToQuery,
+ IN ULONG KeyValueLengthToSet,
+ IN NTSTATUS ExpectedStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes an entry in the array of test keys
+
+Arguments:
+
+ TBS.
+
+Return Value:
+
+--*/
+
+{
+ RegistryKeys[KeyNumber].KeyLevel = KeyLevel;
+ RegistryKeys[KeyNumber].KeyName = KeyName;
+ RegistryKeys[KeyNumber].KeyValueType = KeyValueType;
+ RegistryKeys[KeyNumber].KeyValue = KeyValue;
+ RegistryKeys[KeyNumber].KeyValueLengthToSet = KeyValueLengthToSet;
+ RegistryKeys[KeyNumber].KeyValueLengthToQuery = KeyValueLengthToQuery;
+ RegistryKeys[KeyNumber].ExpectedStatus = ExpectedStatus;
+ RegistryKeys[KeyNumber].KeyHandle = NULL;
+ RegistryKeys[KeyNumber].ParentKeyHandle = NULL;
+
+
+ DBG_UNREFERENCED_PARAMETER (KeyNameLength);
+}
+
+
diff --git a/private/lsa/uclient/ctsamdb.c b/private/lsa/uclient/ctsamdb.c
new file mode 100644
index 000000000..68e690cd2
--- /dev/null
+++ b/private/lsa/uclient/ctsamdb.c
@@ -0,0 +1,213 @@
+
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ctsamdb.c
+
+Abstract:
+
+ CT for loading a SAM Accounts Database
+
+ This test creates a number of users in the local SAM Database
+
+ To build this test:
+
+ cd \nt\private\lsa\uclient
+ nmake UMTYPE=console UMTEST=ctsamdb
+
+ To run this test:
+
+ 1. Build lsasrv.dll with LSA_SAM_ACCOUNTS_DOMAIN_TEST flag
+ enabled in file \nt\private\lsa\server\dbp.h
+
+ 2. On your test system, replace lsasrv.dll in \nt\system32 and reboot.
+
+ 3. Type ctsamdb n to load SAM Database with n users
+
+ 4. Type ctsamdb -1 to delete the users you created.
+
+Author:
+
+ Scott Birrell (ScottBi) October 19, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsaclip.h"
+
+
+/////////////////////////////////////////////////////////////////////////////
+// //
+// LSA Component Test for RPC API - main program //
+// //
+/////////////////////////////////////////////////////////////////////////////
+
+
+VOID
+CtLsaInitObjectAttributes(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the given Object Attributes structure, including
+ Security Quality Of Service. Memory must be allcated for both
+ ObjectAttributes and Security QOS by the caller.
+
+Arguments:
+
+ ObjectAttributes - Pointer to Object Attributes to be initialized.
+
+ SecurityQualityOfService - Pointer to Security QOS to be initialized.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SecurityQualityOfService->Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ SecurityQualityOfService->ImpersonationLevel = SecurityImpersonation;
+ SecurityQualityOfService->ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecurityQualityOfService->EffectiveOnly = FALSE;
+
+ //
+ // Set up the object attributes prior to opening the LSA.
+ //
+
+ InitializeObjectAttributes(
+ ObjectAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL
+ );
+
+ //
+ // The InitializeObjectAttributes macro presently stores NULL for
+ // the SecurityQualityOfService field, so we must manually copy that
+ // structure for now.
+ //
+
+ ObjectAttributes->SecurityQualityOfService = SecurityQualityOfService;
+}
+
+VOID _CRTAPI1
+main (argc, argv)
+int argc;
+char **argv;
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ UNICODE_STRING NumberOfAccounts;
+ ANSI_STRING NumberOfAccountsAnsi;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
+ LSA_HANDLE PolicyHandle;
+
+ if (argc != 2) {
+
+ printf("\n");
+ printf("Instructions for using SAM Accounts Domain Test Load\n");
+ printf("----------------------------------------------------\n");
+ printf("\n\n");
+ printf("This program can be used to create n users in a SAM\n");
+ printf("Accounts domain, or update user information in a domain.\n");
+ printf("Usernames and other information are pseudo-randomized\n");
+ printf("and Relative Ids begin at 4096, to avoid conflict with\n");
+ printf("existing installed accounts\n");
+ printf("\n");
+ printf("NOTE: \\\\popcorn\\public\\scottbi\\runsamdb temporarily\n");
+ printf("contains 340-compatible x86 versions of the four files\n");
+ printf("described in steps 1. 2. and 3 below.\n");
+ printf("\n");
+ printf("1. Replace lsasrv.dll with one compiled with the\n");
+ printf(" LSA_SAM_ACCOUNTS_DOMAIN_TEST #define enabled\n");
+ printf(" in file lsa\\server\\dbpolicy.c.\n");
+ printf("\n");
+ printf("2. Replace samsrv.dll with one containing chads\n");
+ printf(" mondo level SamSetInformationUser changes.\n");
+ printf("\n");
+ printf("3. Copy runsamdb.cmd and ctsamdb.exe to a directory\n");
+ printf(" on your path\n");
+ printf("\n");
+ printf("4. Reboot system with debugger enabled. Debugger terminal\n");
+ printf(" will display a message for each 100 users created\n");
+ printf(" plus the time taken to create the last 100 users.\n");
+ printf(" If any attempt is made to create an existing user,\n");
+ printf(" or a user that conflicts with an existing account, the\n");
+ printf(" total number of occurrences of these to date is displayed.\n");
+ printf("\n");
+ printf("5. To load a SAM database with n > 0 users, type:\n");
+ printf("\n");
+ printf(" runsamdb n\n");
+ printf("\n");
+ printf("6. To update the SAM database with identical information\n");
+ printf(" to that loaded, repeat the command in 5.\n");
+ printf("\n");
+ printf("7. To delete the users you created, type\n");
+ printf("\n");
+ printf(" runsamdb -1\n");
+ printf("\n");
+ printf("8. Existing accounts not created by the test will not\n");
+ printf(" normally be affected.\n");
+ printf("\n");
+ printf("9. To repeat these instructions, type\n");
+ printf("\n");
+ printf(" runsamdb\n");
+ return;
+ }
+
+ RtlInitAnsiString( &NumberOfAccountsAnsi, argv[1] );
+ RtlAnsiStringToUnicodeString(
+ &NumberOfAccounts,
+ &NumberOfAccountsAnsi,
+ TRUE
+ );
+
+ CtLsaInitObjectAttributes(
+ &ObjectAttributes,
+ &SecurityQualityOfService
+ );
+
+ //
+ // Open a handle to the local Policy Object. Use a benign access
+ // mask, because we won't check it.
+ //
+
+ Status = LsaOpenPolicy(
+ NULL,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ printf("LSA RPC CT - LsaOpenPolicy failed 0x%lx\n", Status);
+ return;
+ }
+
+ //
+ // Use an information class in LsaSetInformationPolicy() that can't be
+ // specified normally on a set operation.
+ //
+
+ Status = LsaSetInformationPolicy(
+ PolicyHandle,
+ PolicyPdAccountInformation,
+ &NumberOfAccounts
+ );
+
+ Status = LsaClose( PolicyHandle );
+}
diff --git a/private/lsa/uclient/lsaclip.h b/private/lsa/uclient/lsaclip.h
new file mode 100644
index 000000000..0e56e32e2
--- /dev/null
+++ b/private/lsa/uclient/lsaclip.h
@@ -0,0 +1,25 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lsaclip.h
+
+Abstract:
+
+ LSA - Client Side private includes
+
+Author:
+
+ Scott Birrell (ScottBi) January 23, 1992
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <lsacomp.h>
+#include "lsarpc_c.h"
+#include <rpcndr.h>
diff --git a/private/lsa/uclient/lsaudll.def b/private/lsa/uclient/lsaudll.def
new file mode 100644
index 000000000..c598f1057
--- /dev/null
+++ b/private/lsa/uclient/lsaudll.def
@@ -0,0 +1,11 @@
+LIBRARY LSAUDLL
+
+DESCRIPTION 'Local Security Authority User-Mode Client Library'
+
+EXPORTS
+
+;
+; Scott Birrell (ScottBi) Febreary 14, 1992
+;
+; Exported Public RPC Services
+;
diff --git a/private/lsa/uclient/makefile b/private/lsa/uclient/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/lsa/uclient/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/lsa/uclient/rpcapi.c b/private/lsa/uclient/rpcapi.c
new file mode 100644
index 000000000..2169a078c
--- /dev/null
+++ b/private/lsa/uclient/rpcapi.c
@@ -0,0 +1,3797 @@
+
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rpcapi.c
+
+Abstract:
+
+ This module contains the routines for the LSA API that use RPC. The
+ routines in this module are merely wrappers that work as follows:
+
+ o Client program calls LsaFoo in this module
+ o LsaFoo calls RPC client stub interface routine LsapFoo with
+ similar parameters. Some parameters are translated from types
+ (e.g structures containing PVOIDs or certain kinds of variable length
+ parameters such as pointers to SID's) that are not specifiable on an
+ RPC interface, to specifiable form.
+ o RPC client stub LsapFoo calls interface specific marshalling routines
+ and RPC runtime to marshal parameters into a buffer and send them over
+ to the server side of the LSA.
+ o Server side calls RPC runtime and interface specific unmarshalling
+ routines to unmarshal parameters.
+ o Server side calls worker LsapFoo to perform API function.
+ o Server side marshals response/output parameters and communicates these
+ back to client stub LsapFoo
+ o LsapFoo exits back to LsaFoo which returns to client program.
+
+Author:
+
+ Scott Birrell (ScottBi) April 24, 1991
+
+Revision History:
+
+--*/
+
+#include "lsaclip.h"
+
+//
+// The following limit on the maximum number of Sids or Names is tentative,
+// so it is not being published.
+//
+
+#define LSAP_DB_TRIAL_MAXIMUM_SID_COUNT ((ULONG) 0x00005000L)
+#define LSAP_DB_TRIAL_MAXIMUM_NAME_COUNT ((ULONG) 0x00005000L)
+
+
+//
+// Functions private to this module
+//
+
+NTSTATUS
+LsapApiReturnResult(
+ IN ULONG ExceptionCode
+ );
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Local Security Policy Administration API function prototypes //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+LsaOpenPolicy(
+ IN PUNICODE_STRING SystemName OPTIONAL,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN ACCESS_MASK DesiredAccess,
+ IN OUT PLSA_HANDLE PolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ To administer the Local Security Policy of a local or remote system,
+ this API must be called to establish a session with that system's
+ Local Security Authority (LSA) subsystem. This API connects to
+ the LSA of the target system and opens the object representing
+ the target system's Local Security Policy database. A handle to
+ the object is returned. This handle must be used on all subsequent API
+ calls to administer the Local Security Policy information for the
+ target system.
+
+Arguments:
+
+ SystemName - Name of the target system to be administered.
+ Administration of the local system is assumed if NULL is specified.
+
+ ObjectAttributes - Pointer to the set of attributes to use for this
+ connection. The security Quality Of Service information is used and
+ normally should provide Security Identification level of
+ impersonation. Some operations, however, require Security
+ Impersonation level of impersonation.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the LSA Subsystem's LSA Database. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target LsaDatabase object to determine whether the
+ accesses will be granted or denied.
+
+ PolicyHandle - Receives a handle to be used in future requests to
+ access the Local Security Policy of the target system. This handle
+ represents both the handle to the LsaDatabase object and
+ the RPC Context Handle for the connection to the target LSA
+ susbsystem.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have access to the target
+ system's LSA Database, or does not have other desired accesses.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLSAPR_SERVER_NAME ServerName = NULL;
+ USHORT NullTerminatedServerNameLength;
+
+ RpcTryExcept {
+
+ //
+ // Get the Server Name as a Unicode String buffer. Set it to
+ // NULL (i.e. local machine) if a zero length or NULL Unicode String
+ // structure us passed. If a non NULL server name is given, we must
+ // ensure that it is terminated with a NULL wide character. Allocate
+ // a buffer that is one wide character longer than the server name
+ // buffer, copy the server name to that buffer and append a trailing
+ // NULL wide character.
+ //
+
+ if (ARGUMENT_PRESENT(SystemName) &&
+ (SystemName->Buffer != NULL) &&
+ (SystemName->Length > 0)) {
+
+ NullTerminatedServerNameLength = SystemName->Length + (USHORT) sizeof (WCHAR);
+
+ ServerName = MIDL_user_allocate( NullTerminatedServerNameLength );
+
+ if (ServerName != NULL) {
+
+ RtlZeroMemory( ServerName, NullTerminatedServerNameLength );
+
+ RtlMoveMemory(
+ ServerName,
+ SystemName->Buffer,
+ SystemName->Length
+ );
+
+ } else {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ *PolicyHandle = NULL;
+
+ ObjectAttributes->RootDirectory = NULL;
+
+ Status = LsarOpenPolicy2(
+ ServerName,
+ (PLSAPR_OBJECT_ATTRIBUTES) ObjectAttributes,
+ DesiredAccess,
+ (PLSAPR_HANDLE) PolicyHandle
+ );
+ }
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ //
+ // If the open failed because the new API doesn't exist, try the
+ // old one.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ RpcTryExcept {
+ ASSERT(*PolicyHandle == NULL);
+ ASSERT(ObjectAttributes->RootDirectory == NULL);
+
+ Status = LsarOpenPolicy(
+ ServerName,
+ (PLSAPR_OBJECT_ATTRIBUTES) ObjectAttributes,
+ DesiredAccess,
+ (PLSAPR_HANDLE) PolicyHandle
+ );
+
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ }
+
+ //
+ // If necessary, free the NULL-terminated server name buffer.
+ //
+
+ if (ServerName != NULL) {
+
+ MIDL_user_free( ServerName );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaQueryInformationPolicy(
+ IN LSA_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ OUT PVOID *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaQueryInformationPolicy API obtains information from the Policy
+ object. The caller must have access appropriate to the information
+ being requested (see InformationClass parameter).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ PolicyAuditLogInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyAuditEventsInformation POLICY_VIEW_AUDIT_INFORMATION
+ PolicyPrimaryDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyAccountDomainInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyPdAccountInformation POLICY_GET_PRIVATE_INFORMATION
+ PolicyLsaServerRoleInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyReplicaSourceInformation POLICY_VIEW_LOCAL_INFORMATION
+ PolicyDefaultQuotaInformation POLICY_VIEW_LOCAL_INFORMATION
+
+ Buffer - receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ Others TBS
+--*/
+
+{
+ NTSTATUS Status;
+
+ PLSAPR_POLICY_INFORMATION PolicyInformation = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaQueryInformationPolicy.
+ //
+
+ Status = LsarQueryInformationPolicy(
+ (LSAPR_HANDLE) PolicyHandle,
+ InformationClass,
+ &PolicyInformation
+ );
+
+ //
+ // Return pointer to Policy Information for the given class, or NULL.
+ //
+
+ *Buffer = PolicyInformation;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the returned Policy Information,
+ // free it.
+ //
+
+ if (PolicyInformation != NULL) {
+
+ MIDL_user_free(PolicyInformation);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaSetInformationPolicy(
+ IN LSA_HANDLE PolicyHandle,
+ IN POLICY_INFORMATION_CLASS InformationClass,
+ IN PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetInformationPolicy API modifies information in the Policy Object.
+ The caller must have access appropriate to the information to be changed
+ in the Policy Object, see the InformationClass parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ InformationClass - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ PolicyAuditLogInformation POLICY_AUDIT_LOG_ADMIN
+ PolicyAuditEventsInformation POLICY_SET_AUDIT_REQUIREMENTS
+ PolicyPrimaryDomainInformation POLICY_TRUST_ADMIN
+ PolicyAccountDomainInformation POLICY_TRUST_ADMIN
+ PolicyPdAccountInformation Not settable by this API
+ PolicyLsaServerRoleInformation POLICY_SERVER_ADMIN
+ PolicyReplicaSourceInformation POLICY_SERVER_ADMIN
+ PolicyDefaultQuotaInformation POLICY_SET_DEFAULT_QUOTA_LIMITS
+
+ Buffer - Points to a structure containing the information appropriate
+ to the information type specified by the InformationClass parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaSetInformationPolicy.
+ //
+
+ Status = LsarSetInformationPolicy(
+ (LSAPR_HANDLE) PolicyHandle,
+ InformationClass,
+ (PLSAPR_POLICY_INFORMATION) Buffer
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaClearAuditLog(
+ IN LSA_HANDLE PolicyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function clears the Audit Log. Caller must have POLICY_AUDIT_LOG_ADMIN
+ access to the Policy Object to perform this operation.
+
+Arguments:
+
+ PolicyHandle - handle from an LsaOpenPolicy call.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_ACCESS_DENIED - Caller does not have the required access
+ to perform the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
+ a Policy Object.
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaClearAuditLog.
+ //
+
+ Status = LsarClearAuditLog(
+ (LSAPR_HANDLE) PolicyHandle
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+LsaLookupPrivilegeValue(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING Name,
+ OUT PLUID Value
+ )
+
+/*++
+
+Routine Description:
+
+ This function retrieves the value used on the target system
+ to locally represent the specified privilege. The privilege
+ is specified by programmatic name.
+
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
+ must be open for POLICY_LOOKUP_NAMES access.
+
+ Name - Is the privilege's programmatic name.
+
+ Value - Receives the locally unique ID the privilege is known by on the
+ target machine.
+
+
+
+Return Value:
+
+ NTSTATUS - The privilege was found and returned.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LUID Buffer;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaLookupPrivilegeValue.
+ //
+
+ Status = LsarLookupPrivilegeValue(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_UNICODE_STRING)Name,
+ &Buffer
+ );
+
+ *Value = Buffer;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaLookupPrivilegeName(
+ IN LSA_HANDLE PolicyHandle,
+ IN PLUID Value,
+ OUT PUNICODE_STRING *Name
+ )
+
+/*++
+
+Routine Description:
+
+ This function programmatic name corresponding to the privilege
+ represented on the target system by the provided LUID.
+
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
+ must be open for POLICY_LOOKUP_NAMES access.
+
+ Value - is the locally unique ID the privilege is known by on the
+ target machine.
+
+ Name - Receives the privilege's programmatic name.
+
+
+
+Return Value:
+
+ NTSTATUS - The privilege was found and returned.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PLSAPR_UNICODE_STRING Buffer = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaLookupPrivilegeName.
+ //
+
+ Status = LsarLookupPrivilegeName(
+ (LSAPR_HANDLE) PolicyHandle,
+ Value,
+ &Buffer
+ );
+
+ (*Name) = (PUNICODE_STRING)Buffer;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the return buffer, free it.
+ //
+
+ if (Buffer != NULL) {
+
+ MIDL_user_free(Buffer);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+
+ return(Status);
+
+
+}
+
+
+
+NTSTATUS
+LsaLookupPrivilegeDisplayName(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING Name,
+ OUT PUNICODE_STRING *DisplayName,
+ OUT PSHORT LanguageReturned
+ )
+
+/*++
+
+Routine Description:
+
+ This function retrieves a displayable name representing the
+ specified privilege.
+
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
+ must be open for POLICY_LOOKUP_NAMES access.
+
+ Name - The programmatic privilege name to look up.
+
+ DisplayName - Receives a pointer to the privilege's displayable
+ name.
+
+ LanguageReturned - Receives the language of the returned displayable
+ name.
+
+
+Return Value:
+
+ NTSTATUS - The privilege text was found and returned.
+
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+
+ STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
+ found.
+
+--*/
+{
+
+ NTSTATUS Status;
+ SHORT ClientLanguage, ClientSystemDefaultLanguage;
+ PLSAPR_UNICODE_STRING Buffer = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaLookupPrivilegeDisplayName.
+ //
+
+ ClientLanguage = (SHORT)NtCurrentTeb()->CurrentLocale;
+ ClientSystemDefaultLanguage = ClientLanguage; //no sys default yet
+ Status = LsarLookupPrivilegeDisplayName(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_UNICODE_STRING)Name,
+ ClientLanguage,
+ ClientSystemDefaultLanguage,
+ &Buffer,
+ (PWORD)LanguageReturned
+ );
+
+ (*DisplayName) = (PUNICODE_STRING)Buffer;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the return buffer, free it.
+ //
+
+ if (Buffer != NULL) {
+
+ MIDL_user_free(Buffer);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+
+ return(Status);
+}
+
+
+
+
+NTSTATUS
+LsaClose(
+ IN LSA_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This API closes a handle to the LsaDatabase object or open object within
+ the database. If a handle to the LsaDatabase object is closed and there
+ are no objects still open within the current connection to the LSA, the
+ connection is closed. If a handle to an object within the database is
+ closed and the object is marked for DELETE access, the object will be
+ deleted when the last handle to that object is closed.
+
+Arguments:
+
+ ObjectHandle - This parameter is either a handle to the LsaDatabase
+ object, which represents the entire LSA Database and also a
+ connection to the LSA of a target system, or a handle to an
+ object within the database.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSAPR_HANDLE Handle = (LSAPR_HANDLE) ObjectHandle;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaClose. Note that an additional
+ // level of indirection for the context handle parameter is required
+ // for the stub, because the server returns a NULL pointer to the handle
+ // so that the handle will be unbound by the stub.
+ //
+
+ Status = LsarClose( &Handle );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+ if ((Status != RPC_NT_SS_CONTEXT_MISMATCH) &&
+ (Status != RPC_NT_INVALID_BINDING)) {
+ (void) RpcSsDestroyClientContext(&Handle);
+ }
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+
+NTSTATUS
+LsaDelete(
+ IN LSA_HANDLE ObjectHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaDelete API deletes an object. The object must be
+ open for DELETE access.
+
+Arguments:
+
+ ObjectHandle - Handle from an LsaOpen<object-type> call.
+
+ None.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified handle is not valid.
+
+ Result codes from RPC.
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ //
+ // Try calling the new worker routine LsarDeleteObject(). If
+ // this fails because it does not exist (versions 1.369 and earlier)
+ // then call the old routine LsarDelete().
+ //
+
+ Status = LsarDeleteObject((LSAPR_HANDLE *) &ObjectHandle);
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ RpcTryExcept {
+
+ Status = LsarDelete((LSAPR_HANDLE) ObjectHandle);
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaQuerySecurityObject(
+ IN LSA_HANDLE ObjectHandle,
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaQuerySecurityObject API returns security information assigned
+ to an LSA Database object.
+
+ Based on the caller's access rights and privileges, this procedure will
+ return a security descriptor containing any or all of the object's owner
+ ID, group ID, discretionary ACL or system ACL. To read the owner ID,
+ group ID, or the discretionary ACL, the caller must be granted
+ READ_CONTROL access to the object. To read the system ACL, the caller must
+ have SeSecurityPrivilege privilege.
+
+ This API is modelled after the NtQuerySecurityObject() system service.
+
+Arguments:
+
+ ObjectHandle - A handle to an existing object in the LSA Database.
+
+ SecurityInformation - Supplies a value describing which pieces of
+ security information are being queried. The values that may be
+ specified are the same as those defined in the NtSetSecurityObject()
+ API section.
+
+ SecurityDescriptor - receives a pointer to a buffer containing the
+ requested security information. This information is returned in
+ the form of a security descriptor. The caller is responsible for
+ freeing the returned buffer using LsaFreeMemory() when no longer
+ needed.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
+--*/
+
+{
+ NTSTATUS Status;
+ LSAPR_SR_SECURITY_DESCRIPTOR ReturnedSD;
+ PLSAPR_SR_SECURITY_DESCRIPTOR PReturnedSD;
+
+ //
+ // The retrieved security descriptor is returned via a data structure that
+ // looks like:
+ //
+ // +-----------------------+
+ // | Length (bytes) |
+ // |-----------------------| +--------------+
+ // | SecurityDescriptor ---|--------->| Self-Relative|
+ // +-----------------------+ | Security |
+ // | Descriptor |
+ // +--------------+
+ //
+ // The first of these buffers is a local stack variable. The buffer containing
+ // the self-relative security descriptor is allocated by the RPC runtime. The
+ // pointer to the self-relative security descriptor is what is passed back to our
+ // caller.
+ //
+ //
+
+ //
+ // To prevent RPC from trying to marshal a self-relative security descriptor,
+ // make sure its field values are appropriately initialized to zero and null.
+ //
+
+ ReturnedSD.Length = 0;
+ ReturnedSD.SecurityDescriptor = NULL;
+
+ //
+ // Call the server ...
+ //
+
+
+ RpcTryExcept{
+
+ PReturnedSD = &ReturnedSD;
+
+ Status = LsarQuerySecurityObject(
+ (LSAPR_HANDLE) ObjectHandle,
+ SecurityInformation,
+ &PReturnedSD
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ (*SecurityDescriptor) = ReturnedSD.SecurityDescriptor;
+
+ } else {
+
+ (*SecurityDescriptor) = NULL;
+ }
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecurityObjectError;
+ }
+
+QuerySecurityObjectFinish:
+
+ return(Status);
+
+QuerySecurityObjectError:
+
+ goto QuerySecurityObjectFinish;
+}
+
+
+NTSTATUS
+LsaSetSecurityObject(
+ IN LSA_HANDLE ObjectHandle,
+ IN SECURITY_INFORMATION SecurityInformation,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetSecurityObject API takes a well formaed Security Descriptor
+ and assigns specified portions of it to an object. Based on the flags set
+ in the SecurityInformation parameter and the caller's access rights, this
+ procedure will replace any or alll of the security information associated
+ with the object.
+
+ The caller must have WRITE_OWNER access to the object to change the
+ owner or Primary group of the object. The caller must have WRITE_DAC
+ access to the object to change the Discretionary ACL. The caller must
+ have SeSecurityPrivilege to assign a system ACL to an object.
+
+ This API is modelled after the NtSetSecurityObject() system service.
+
+Arguments:
+
+ ObjectHandle - A handle to an existing object in the LSA Database.
+
+ SecurityInformation - Indicates which security information is to be
+ applied to the object. The values that may be specified are the
+ same as those defined in the NtSetSecurityObject() API section.
+ The value(s) to be assigned are passed in the SecurityDescriptor
+ parameter.
+
+ SecurityDescriptor - A pointer to a well formed Security Descriptor.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_PARAMETER - An invalid parameter has been specified.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG SDLength;
+ LSAPR_SR_SECURITY_DESCRIPTOR DescriptorToPass;
+
+ //
+ // Make a self relative security descriptor for use in the RPC call..
+ //
+
+ SDLength = 0;
+
+ Status = RtlMakeSelfRelativeSD( SecurityDescriptor, NULL, &SDLength);
+
+ if (Status != STATUS_BUFFER_TOO_SMALL) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto SetSecurityObjectError;
+ }
+
+ DescriptorToPass.SecurityDescriptor = MIDL_user_allocate( SDLength );
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (DescriptorToPass.SecurityDescriptor == NULL) {
+
+ goto SetSecurityObjectError;
+
+ }
+
+ //
+ // Make an appropriate self-relative security descriptor
+ //
+
+ Status = RtlMakeSelfRelativeSD(
+ SecurityDescriptor,
+ (PSECURITY_DESCRIPTOR)DescriptorToPass.SecurityDescriptor,
+ &SDLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+
+ DescriptorToPass.Length = SDLength;
+
+ RpcTryExcept{
+
+ Status = LsarSetSecurityObject(
+ (LSAPR_HANDLE) ObjectHandle,
+ SecurityInformation,
+ &DescriptorToPass
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecurityObjectError;
+ }
+
+SetSecurityObjectFinish:
+
+ //
+ // If necessary, free the Self Relative SD passed to the worker.
+ //
+
+ if (DescriptorToPass.SecurityDescriptor != NULL) {
+
+ MIDL_user_free( DescriptorToPass.SecurityDescriptor );
+
+ DescriptorToPass.SecurityDescriptor = NULL;
+ }
+
+ return(Status);
+
+SetSecurityObjectError:
+
+ goto SetSecurityObjectFinish;
+}
+
+
+NTSTATUS
+LsaChangePassword(
+ IN PUNICODE_STRING ServerName,
+ IN PUNICODE_STRING DomainName,
+ IN PUNICODE_STRING AccountName,
+ IN PUNICODE_STRING OldPassword,
+ IN PUNICODE_STRING NewPassword
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaChangePassword API is used to change a user account's password.
+ The user must have appropriate access to the user account and must
+ know the current password value.
+
+
+Arguments:
+
+ ServerName - The name of the Domain Controller at which the password
+ can be changed.
+
+ DomainName - The name of the domain in which the account exists.
+
+ AccountName - The name of the account whose password is to be changed.
+
+ NewPassword - The new password value.
+
+ OldPassword - The old (current) password value.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, e.g.
+ contains characters that can't be entered from the keyboard.
+
+ STATUS_PASSWORD_RESTRICTION - A restriction prevents the password
+ from being changed. This may be for an number of reasons,
+ including time restrictions on how often a password may be changed
+ or length restrictions on the provided (new) 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.
+
+ STATUS_WRONG_PASSWORD - OldPassword does not contain the user's
+ current password.
+
+ STATUS_NO_SUCH_USER - The SID provided does not lead to a user
+ account.
+
+ STATUS_CANT_UPDATE_MASTER - An attempt to update the master copy
+ of the password was unsuccessful. Please try again later.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ DBG_UNREFERENCED_PARAMETER( ServerName );
+ DBG_UNREFERENCED_PARAMETER( DomainName );
+ DBG_UNREFERENCED_PARAMETER( AccountName );
+ DBG_UNREFERENCED_PARAMETER( OldPassword );
+ DBG_UNREFERENCED_PARAMETER( NewPassword );
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaCreateAccount(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE AccountHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaCreateAccount API adds a user or group account to the
+ list of accounts in the target system's LsaDatabase object. The
+ newly added account object is initially placed in the opened state and
+ a handle to it is returned. The caller must have LSA_CREATE_ACCOUNT
+ access to the LsaDatabase object.
+
+ Note that no check is made to determine whether there is an account
+ of the given Sid in the target system's Primary Domain (if any), nor
+ is any check made to verify that the Sid and name describe the same
+ account.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenLsa call.
+
+ AccountSid - Points to the SID of the Account object.
+
+ DesiredAccess - Specifies the accesses to be granted to the newly
+ created and opened account.
+
+ AccountHandle - Receives a handle to the newly created and opened
+ account. This handle is used on subsequent accesses to the account
+ until closed.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_ACCOUNT_ALREADY_EXISTS - A user or group account object having
+ the Sid given in AccountInformation already exists.
+
+ STATUS_INVALID_PARAMETER - An invalid parameter has been specified,
+ one or more of the following apply.
+
+ - CreateDisposition not valid
+ - A user or group account having the Sid given AccountInformation
+ already exists, but CreateDisposition = LSA_OBJECT_CREATE.
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ Status = LsarCreateAccount(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) AccountSid,
+ DesiredAccess,
+ (PLSAPR_HANDLE) AccountHandle
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaEnumerateAccounts(
+ IN LSA_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PVOID *Buffer,
+ IN ULONG PreferedMaximumLength,
+ OUT PULONG CountReturned
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaEnumerateAccounts API returns information about
+ Account objects. This call requires
+ POLICY_VIEW_LOCAL_INFORMATION access to the Policy object. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information. To
+ support this feature, the caller is provided with a handle that can
+ be used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenLsa call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ EnumerationInformation - Receives a pointer to an array of structures
+ each describing an Account object. Currently, each structure contains
+ a pointer to the Account Sid.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+ CountReturned - Pointer to location which receives the number of entries
+ returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully, there may be
+ more entries.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if there are no more objects to enumerate. Note that
+ one or more objects may be enumerated on a call that returns this
+ reply.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter.
+
+ - NULL return pointer for enumeration buffer.
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer;
+
+ EnumerationBuffer.EntriesRead = 0;
+ EnumerationBuffer.Information = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Enumerate the Accounts. On successful return,
+ // the Enumeration Buffer structure will receive a count
+ // of the number of Accounts enumerated this call
+ // and a pointer to an array of Account Information Entries.
+ //
+ // EnumerationBuffer -> EntriesRead
+ // Information -> Account Info for Domain 0
+ // Account Info for Domain 1
+ // ...
+ // Account Info for Domain
+ // (EntriesRead - 1)
+ //
+
+ Status = LsarEnumerateAccounts(
+ (LSAPR_HANDLE) PolicyHandle,
+ EnumerationContext,
+ &EnumerationBuffer,
+ PreferedMaximumLength
+ );
+
+ //
+ // Return enumeration information or NULL to caller.
+ //
+ // NOTE: "Information" is allocated by the called client stub
+ // as a single block via MIDL_user_allocate, because Information is
+ // allocated all-nodes. We can therefore pass back the pointer
+ // directly to the client, who will be able to free the memory after
+ // use via LsaFreeMemory() [which makes a MIDL_user_free call].
+ //
+
+ *CountReturned = EnumerationBuffer.EntriesRead;
+ *Buffer = EnumerationBuffer.Information;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the Account Information array,
+ // free it.
+ //
+
+ if (EnumerationBuffer.Information != NULL) {
+
+ MIDL_user_free(EnumerationBuffer.Information);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaCreateTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN PLSA_TRUST_INFORMATION TrustedDomainInformation,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE TrustedDomainHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaCreateTrustedDomain API creates a new TrustedDomain object. The
+ caller must have POLICY_TRUST_ADMIN access to the Policy Object.
+
+ Note that NO verification is done to check that the given domain name
+ matches the given SID or that the SID or name represent an actual domain.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainInformation - Pointer to structure containing the name and
+ SID of the new Trusted Domain.
+
+ DesiredAccess - Specifies the accesses to be granted for the newly
+ created object.
+
+ TrustedDomainHandle - receives a handle referencing the newly created
+ object. This handle is used on subsequent accesses to the object.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ *TrustedDomainHandle = NULL;
+
+ RpcTryExcept {
+
+ Status = LsarCreateTrustedDomain(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_TRUST_INFORMATION) TrustedDomainInformation,
+ DesiredAccess,
+ (PLSAPR_HANDLE) TrustedDomainHandle
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaOpenTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE TrustedDomainHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaOpenTrustedDomain API opens an existing TrustedDomain object
+ using the SID as the primary key value.
+
+Arguments:
+
+ PolicyHandle - An open handle to a Policy object.
+
+ TrustedDomainSid - Pointer to the account's Sid.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested to the target object.
+
+ TrustedDomainHandle - Receives a handle to be used in future requests.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_TRUSTED_DOMAIN_NOT_FOUND - There is no TrustedDomain object in the
+ target system's LSA Database having the specified AccountSid.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ Status = LsarOpenTrustedDomain(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) TrustedDomainSid,
+ DesiredAccess,
+ (PLSAPR_HANDLE) TrustedDomainHandle
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaQueryInfoTrustedDomain(
+ IN LSA_HANDLE TrustedDomainHandle,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ OUT PVOID *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaQueryInfoTrustedDomain API obtains information from a
+ TrustedDomain object. The caller must have access appropriate to the
+ information being requested (see InformationClass parameter).
+
+Arguments:
+
+ TrustedDomainHandle - Handle from an LsaOpenTrustedDomain or
+ LsaCreateTrustedDomain call.
+
+ InformationClass - Specifies the information to be returned. The
+ Information Classes and accesses required are as follows:
+
+ Information Class Required Access Type
+
+ TrustedAccountNameInformation TRUSTED_QUERY_ACCOUNT_NAME
+ TrustedControllersInformation TRUSTED_QUERY_CONTROLLERS
+ TrustedPosixInformation TRUSTED_QUERY_POSIX
+
+ Buffer - Receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+
+{
+ NTSTATUS Status;
+
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaQueryInformationTrustedDomain.
+ //
+
+ Status = LsarQueryInfoTrustedDomain(
+ (LSAPR_HANDLE) TrustedDomainHandle,
+ InformationClass,
+ &TrustedDomainInformation
+ );
+
+ //
+ // Return pointer to Policy Information for the given class, or NULL.
+ //
+
+ *Buffer = TrustedDomainInformation;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the returned Trusted Domain Information,
+ // free it.
+ //
+
+ if (TrustedDomainInformation != NULL) {
+
+ MIDL_user_free(TrustedDomainInformation);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaSetInformationTrustedDomain(
+ IN LSA_HANDLE TrustedDomainHandle,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetInformationTrustedDomain API modifies information in the Trusted
+ Domain Object. The caller must have access appropriate to the
+ information to be changedin the Policy Object, see the InformationClass
+ parameter.
+
+Arguments:
+
+ TrustedDomainHandle - Handle from an LsaOpenTrustedDomain or
+ LsaCreateTrustedDomain call.
+
+ InformationClass - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ TrustedAccountInformation ( Cannot be set )
+ TrustedControllersInformation TRUSTED_SET_CONTROLLERS
+ TrustedPosixOffsetInformation TRUSTED_POSIX_INFORMATION
+
+ Buffer - Points to a structure containing the information appropriate
+ to the InformationClass parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - Call completed successfully.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INVALID_HANDLE - Handle is invalid or is of the wrong type.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter:
+ Information class invalid
+ Information class cannot be set
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaSetInformationTrustedDomain
+ //
+
+ Status = LsarSetInformationTrustedDomain(
+ (LSAPR_HANDLE) TrustedDomainHandle,
+ InformationClass,
+ (PLSAPR_TRUSTED_DOMAIN_INFO) Buffer
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaEnumerateTrustedDomains(
+ IN LSA_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PVOID *Buffer,
+ IN ULONG PreferedMaximumLength,
+ OUT PULONG CountReturned
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaEnumerateTrustedDomains API returns information about the accounts
+ in the target system's Policy object. This call requires
+ POLICY_VIEW_LOCAL_INFORMATION access to the Policy object. Since there
+ may be more information than can be returned in a single call of the
+ routine, multiple calls can be made to get all of the information. To
+ support this feature, the caller is provided with a handle that can
+ be used across calls to the API. On the initial call, EnumerationContext
+ should point to a variable that has been initialized to 0.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ EnumerationContext - API-specific handle to allow multiple calls
+ (see Routine Description above).
+
+ Buffer - Receives a pointer to a buffer containing enumeration
+ information. This buffer is an array of structures of type
+ LSA_TRUST_INFORMATION. If no trusted domains are found,
+ NULL is returned.
+
+ PreferedMaximumLength - Prefered maximum length of returned data (in 8-bit
+ bytes). This is not a hard upper limit, but serves as a guide. Due to
+ data conversion between systems with different natural data sizes, the
+ actual amount of data returned may be greater than this value.
+
+ CountReturned - Pointer to variable which will receive a count of the
+ entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call completed successfully, there may be
+ more entries.
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if there are no more objects to enumerate. Note that
+ one or more objects may be enumerated on a call that returns this
+ reply.
+
+ STATUS_INVALID_PARAMETER - Invalid parameter.
+
+ - NULL return pointer for enumeration buffer.
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer;
+ EnumerationBuffer.EntriesRead = 0;
+ EnumerationBuffer.Information = NULL;
+
+ //
+ // Verify that caller has provided a return buffer pointer.
+ //
+
+ if (!ARGUMENT_PRESENT(Buffer)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+ RpcTryExcept {
+
+ //
+ // Enumerate the Trusted Domains. On successful return,
+ // the Enumeration Buffer structure will receive a count
+ // of the number of Trusted Domains enumerated this call
+ // and a pointer to an array of Trust Information Entries.
+ //
+ // EnumerationBuffer -> EntriesRead
+ // Information -> Trust Info for Domain 0
+ // Trust Info for Domain 1
+ // ...
+ // Trust Info for Domain
+ // (EntriesRead - 1)
+ //
+ //
+
+ Status = LsarEnumerateTrustedDomains(
+ (LSAPR_HANDLE) PolicyHandle,
+ EnumerationContext,
+ &EnumerationBuffer,
+ PreferedMaximumLength
+ );
+
+ //
+ // Return enumeration information or NULL to caller.
+ //
+ // NOTE: "Information" is allocated by the called client stub
+ // as a single block via MIDL_user_allocate, because Information is
+ // allocated all-nodes. We can therefore pass back the pointer
+ // directly to the client, who will be able to free the memory after
+ // use via LsaFreeMemory() [which makes a MIDL_user_free call].
+ //
+
+ *CountReturned = EnumerationBuffer.EntriesRead;
+ *Buffer = EnumerationBuffer.Information;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the Trust Information array,
+ // free it.
+ //
+
+ if (EnumerationBuffer.Information != NULL) {
+
+ MIDL_user_free(EnumerationBuffer.Information);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+
+ return Status;
+}
+
+NTSTATUS
+LsaEnumeratePrivileges(
+ IN LSA_HANDLE PolicyHandle,
+ IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
+ OUT PVOID *Buffer,
+ IN ULONG PreferedMaximumLength,
+ OUT PULONG CountReturned
+ )
+
+/*++
+
+Routine Description:
+
+ This function returnes information about privileges known on this
+ system. This call requires POLICY_VIEW_LOCAL_INFORMATION access
+ to the Policy Object. Since there may be more information than
+ can be returned in a single call of the routine, multiple calls
+ can be made to get all of the information. To support this feature,
+ the caller is provided with a handle that can be used across calls to
+ the API. On the initial call, EnumerationContext should point to a
+ variable that has been initialized to 0.
+
+ WARNING! CURRENTLY, THIS FUNCTION ONLY RETURNS INFORMATION ABOUT
+ WELL-KNOWN PRIVILEGES. LATER, IT WILL RETURN INFORMATION
+ ABOUT LOADED PRIVILEGES.
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy() call.
+
+ EnumerationContext - API specific handle to allow multiple calls
+ (see Routine Description).
+
+ Buffer - Receives a pointer to a buffer containing information for
+ one or more Privileges. This information is an array of structures
+ of type POLICY_PRIVILEGE_DEFINITION.
+
+ When this information is no longer needed, it must be released by
+ passing the returned pointer to LsaFreeMemory().
+
+ PreferedMaximumLength - Prefered maximim length of returned data
+ (in 8-bit bytes). This is not a hard upper limit, but serves as
+ a guide. Due to data conversion between systems with different
+ natural data sizes, the actual amount of data returned may be
+ greater than this value.
+
+ CountReturned - Number of entries returned.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code.
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+
+ STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
+ a Policy object.
+
+ STATUS_ACCESS_DENIED - The caller does not have the necessary
+ access to perform the operation.
+
+ STATUS_MORE_ENTRIES - There are more entries, so call again. This
+ is an informational status only.
+
+ STATUS_NO_MORE_ENTRIES - No entries were returned because there
+ are no more.
+
+ Errors from RPC.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer;
+
+ EnumerationBuffer.Entries = 0;
+ EnumerationBuffer.Privileges = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Enumerate the Privileges. On successful return,
+ // the Enumeration Buffer structure will receive a count
+ // of the number of Privileges enumerated this call
+ // and a pointer to an array of Privilege Definition Entries.
+ //
+ // EnumerationBuffer -> Entries
+ // Privileges -> Privilege Definition 0
+ // Privilege Definition 1
+ // ...
+ // Privilege Definition
+ // (Entries - 1)
+ //
+
+ Status = LsarEnumeratePrivileges(
+ (LSAPR_HANDLE) PolicyHandle,
+ EnumerationContext,
+ &EnumerationBuffer,
+ PreferedMaximumLength
+ );
+
+ //
+ // Return enumeration information or NULL to caller.
+ //
+ // NOTE: "Information" is allocated by the called client stub
+ // as a single block via MIDL_user_allocate, because Information is
+ // allocated all-nodes. We can therefore pass back the pointer
+ // directly to the client, who will be able to free the memory after
+ // use via LsaFreeMemory() [which makes a MIDL_user_free call].
+ //
+
+ *CountReturned = EnumerationBuffer.Entries;
+ *Buffer = EnumerationBuffer.Privileges;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the Account Information array,
+ // free it.
+ //
+
+ if (EnumerationBuffer.Privileges != NULL) {
+
+ MIDL_user_free(EnumerationBuffer.Privileges);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaCreateSecret(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING SecretName,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE SecretHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaCreateSecretInLsa API creates a named Secret object in the
+ Lsa Database. Each Secret Object can have two values assigned,
+ called the Current Value and the Old Value. The meaning of these
+ values is known to the Secret object creator. The caller must have
+ LSA_CREATE_SECRET access to the LsaDatabase object.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenLsa call.
+
+ SecretName - Pointer to Unicode String specifying the name of the
+ secret.
+
+ DesiredAccess - Specifies the accesses to be granted to the newly
+ created and opened secret.
+
+ SecretHandle - Receives a handle to the newly created and opened
+ Secret object. This handle is used on subsequent accesses to
+ the object until closed.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_COLLISION - A Secret object having the given name
+ already exists.
+
+ STATUS_TOO_MANY_SECRETS - The maximum number of Secret objects in the
+ system has been reached.
+--*/
+
+{
+ NTSTATUS Status;
+
+ *SecretHandle = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Verify that the given SecretName has non-null length. Currently
+ // midl cannot handle this.
+ //
+
+ if ((SecretName == NULL) ||
+ (SecretName->Buffer == NULL) ||
+ (SecretName->Length == 0) ||
+ (SecretName->Length > SecretName->MaximumLength)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ Status = LsarCreateSecret(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_UNICODE_STRING) SecretName,
+ DesiredAccess,
+ (PLSAPR_HANDLE) SecretHandle
+ );
+ }
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaLookupNames(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG Count,
+ IN PUNICODE_STRING Names,
+ OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ OUT PLSA_TRANSLATED_SID *Sids
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaLookupNames API attempts to translate names of domains, users,
+ groups or aliases to Sids. The caller must have POLICY_LOOKUP_NAMES
+ access to the Policy object.
+
+ Names may be either isolated (e.g. JohnH) or composite names containing
+ both the domain name and account name. Composite names must include a
+ backslash character separating the domain name from the account name
+ (e.g. Acctg\JohnH). An isolated name may be either an account name
+ (user, group, or alias) or a domain name.
+
+ Translation of isolated names introduces the possibility of name
+ collisions (since the same name may be used in multiple domains). An
+ isolated name will be translated using the following algorithm:
+
+ If the name is a well-known name (e.g. Local or Interactive), then the
+ corresponding well-known Sid is returned.
+
+ If the name is the Built-in Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Account Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Primary Domain's name, then that domain's Sid will
+ be returned.
+
+ If the name is a user, group, or alias in the Built-in Domain, then the
+ Sid of that account is returned.
+
+ If the name is a user, group, or alias in the Primary Domain, then the
+ Sid of that account is returned.
+
+ Otherwise, the name is not translated.
+
+ NOTE: Proxy, Machine, and Trust user accounts are not referenced
+ for name translation. Only normal user accounts are used for ID
+ translation. If translation of other account types is needed, then
+ SAM services should be used directly.
+
+Arguments:
+
+ This function is the LSA server RPC worker routine for the
+ LsaLookupNamesInLsa API.
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ Count - Specifies the number of names to be translated.
+
+ Names - Pointer to an array of Count Unicode String structures
+ specifying the names to be looked up and mapped to Sids.
+ The strings may be names of User, Group or Alias accounts or
+ domains.
+
+ ReferencedDomains - receives a pointer to a structure describing the
+ domains used for the translation. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for
+ each translated name, this structure will only contain one
+ component for each domain utilized in the translation.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ Sids - Receives a pointer to an array of records describing each
+ translated Sid. The nth entry in this array provides a translation
+ for (the nth element in the Names parameter.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
+ not be mapped. This is an informational status only.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the call.
+
+ STATUS_TOO_MANY_NAMES - Too many Names have been specified.
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG MappedCount = 0;
+
+ Status = LsaICLookupNames(
+ PolicyHandle,
+ Count,
+ Names,
+ (PLSA_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
+ Sids,
+ LsapLookupWksta,
+ &MappedCount
+ );
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaICLookupNames(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG Count,
+ IN PUNICODE_STRING Names,
+ OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ IN OUT PLSA_TRANSLATED_SID *Sids,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the internal client side version of the LsaLookupNames
+ API. It is called both from the client side of the Lsa and also
+ the server side of the LSA (when calling out to another LSA). The
+ function is identical to the LsaLookupNames API except that there is an
+ additional parameter, the LookupLevel parameter.
+
+ The LsaLookupNames API attempts to translate names of domains, users,
+ groups or aliases to Sids. The caller must have POLICY_LOOKUP_NAMES
+ access to the Policy object.
+
+ Names may be either isolated (e.g. JohnH) or composite names containing
+ both the domain name and account name. Composite names must include a
+ backslash character separating the domain name from the account name
+ (e.g. Acctg\JohnH). An isolated name may be either an account name
+ (user, group, or alias) or a domain name.
+
+ Translation of isolated names introduces the possibility of name
+ collisions (since the same name may be used in multiple domains). An
+ isolated name will be translated using the following algorithm:
+
+ If the name is a well-known name (e.g. Local or Interactive), then the
+ corresponding well-known Sid is returned.
+
+ If the name is the Built-in Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Account Domain's name, then that domain's Sid
+ will be returned.
+
+ If the name is the Primary Domain's name, then that domain's Sid will
+ be returned.
+
+ If the name is a user, group, or alias in the Built-in Domain, then the
+ Sid of that account is returned.
+
+ If the name is a user, group, or alias in the Primary Domain, then the
+ Sid of that account is returned.
+
+ Otherwise, the name is not translated.
+
+ NOTE: Proxy, Machine, and Trust user accounts are not referenced
+ for name translation. Only normal user accounts are used for ID
+ translation. If translation of other account types is needed, then
+ SAM services should be used directly.
+
+Arguments:
+
+ This function is the LSA server RPC worker routine for the
+ LsaLookupNamesInLsa API.
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ Count - Specifies the number of names to be translated.
+
+ Names - Pointer to an array of Count Unicode String structures
+ specifying the names to be looked up and mapped to Sids.
+ The strings may be names of User, Group or Alias accounts or
+ domains.
+
+ ReferencedDomains - receives a pointer to a structure describing the
+ domains used for the translation. The entries in this structure
+ are referenced by the structure returned via the Sids parameter.
+ Unlike the Sids parameter, which contains an array entry for
+ each translated name, this structure will only contain one
+ component for each domain utilized in the translation.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ Sids - Receives a pointer to an array of records describing each
+ translated Sid. The nth entry in this array provides a translation
+ for (the nth element in the Names parameter.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ LookupLevel - Specifies the Level of Lookup to be performed on the
+ target machine. Values of this field are are follows:
+
+ LsapLookupWksta - First Level Lookup performed on a workstation
+ normally configured for Windows-Nt. The lookup searches the
+ Well-Known Sids/Names, and the Built-in Domain and Account Domain
+ in the local SAM Database. If not all Sids or Names are
+ identified, performs a "handoff" of a Second level Lookup to the
+ LSA running on a Controller for the workstation's Primary Domain
+ (if any).
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids or Names are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ MappedCount - Pointer to location that contains a count of the Names
+ mapped so far. On exit, this count will be updated.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_SOME_NOT_MAPPED - Some or all of the names provided could
+ not be mapped. This is an informational status only.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
+ to complete the call.
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN SidsArraySpecified = FALSE;
+
+ LSAPR_TRANSLATED_SIDS ReturnedSids;
+
+ //
+ // Check that we have not specfied more than the maximum number of names
+ // allowed.
+ //
+
+ if (Count > LSAP_DB_TRIAL_MAXIMUM_NAME_COUNT) {
+
+ return(STATUS_TOO_MANY_NAMES);
+ }
+
+ RpcTryExcept {
+
+ //
+ // If this is a Workstation-Level lookup, the Sids and
+ // ReferencedDomain Lists have not been created. Since these
+ // are input parameters in the general case, we need to set them
+ // to NULL.
+ //
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ *ReferencedDomains = NULL;
+ *Sids = NULL;
+ }
+
+ //
+ // There may already be a Sid translation array in cases where
+ // we are called internally (i.e. at a higher LookupLevel than
+ // LsapLookupWksta). Initialize the ReturnedSids structure
+ // accordingly.
+ //
+
+ ReturnedSids.Entries = Count;
+ ReturnedSids.Sids = *Sids;
+
+ if (*Sids != NULL) {
+
+ SidsArraySpecified = TRUE;
+ }
+
+ //
+ // Initiate Lookup of the Sids at the First Level (Workstation
+ // Level).
+ //
+
+ Status = LsarLookupNames(
+ (LSAPR_HANDLE) PolicyHandle,
+ Count,
+ (PLSAPR_UNICODE_STRING) Names,
+ (PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
+ &ReturnedSids,
+ LookupLevel,
+ MappedCount
+ );
+
+ //
+ // Delay checking Status till we leave try clause.
+ //
+
+ //
+ // Return pointer to array of Sid translations, or NULL.
+ //
+ // NOTE: The array of Sid translations is allocated by the called
+ // client stub as a single block via MIDL_user_allocate, because
+ // Information is allocated all-nodes. We can therefore pass back the pointer
+ // directly to the client, who will be able to free the memory after
+ // use via LsaFreeMemory() [which makes a MIDL_user_free call].
+ //
+
+ *Sids = (PLSA_TRANSLATED_SID) ReturnedSids.Sids;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the Sid translation array,
+ // free it.
+ //
+
+ if ((!SidsArraySpecified) && ReturnedSids.Sids != NULL) {
+
+ MIDL_user_free( ReturnedSids.Sids );
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaLookupSids(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG Count,
+ IN PSID *Sids,
+ OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ OUT PLSA_TRANSLATED_NAME *Names
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaLookupSids API attempts to find names corresponding to Sids.
+ If a name can not be mapped to a Sid, the Sid is converted to character
+ form. The caller must have POLICY_LOOKUP_NAMES access to the Policy
+ object.
+
+ WARNING: This routine allocates memory for its output. The caller is
+ responsible for freeing this memory after use. See description of the
+ Names parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ Count - Specifies the number of Sids to be translated.
+
+ Sids - Pointer to an array of Count pointers to Sids to be mapped
+ to names. The Sids may be well_known SIDs, SIDs of User accounts
+ Group Accounts, Alias accounts, or Domains.
+
+ ReferencedDomains - Receives a pointer to a structure describing the
+ domains used for the translation. The entries in this structure
+ are referenced by the strutcure returned via the Names parameter.
+ Unlike the Names paraemeter, which contains an array entry
+ for (each translated name, this strutcure will only contain
+ component for each domain utilized in the translation.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ Names - Receives a pointer to array records describing each translated
+ name. The nth entry in this array provides a translation for
+ the nth entry in the Sids parameter.
+
+ All of the returned names will be isolated names or NULL strings
+ (domain names are returned as NULL strings). If the caller needs
+ composite names, they can be generated by prepending the
+ isolated name with the domain name and a backslash. For example,
+ if (the name Sally is returned, and it is from the domain Manufact,
+ then the composite name would be "Manufact" + "\" + "Sally" or
+ "Manufact\Sally".
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ If a Sid is not translatable, then the following will occur:
+
+ 1) If the SID's domain is known, then a reference domain record
+ will be generated with the domain's name. In this case, the
+ name returned via the Names parameter is a Unicode representation
+ of the relative ID of the account, such as "(314)" or the null
+ string, if the Sid is that of a domain. So, you might end up
+ with a resultant name of "Manufact\(314) for the example with
+ Sally above, if Sally's relative id is 314.
+
+ 2) If not even the SID's domain could be located, then a full
+ Unicode representation of the SID is generated and no domain
+ record is referenced. In this case, the returned string might
+ be something like: "(S-1-672194-21-314)".
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_SOME_NOT_MAPPED - Some or all of the names provided could not be
+ mapped. This is a warning only.
+
+ Rest TBS
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG MappedCount = 0;
+
+ Status = LsaICLookupSids(
+ PolicyHandle,
+ Count,
+ Sids,
+ ReferencedDomains,
+ Names,
+ LsapLookupWksta,
+ &MappedCount
+ );
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaICLookupSids(
+ IN LSA_HANDLE PolicyHandle,
+ IN ULONG Count,
+ IN PSID *Sids,
+ OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
+ IN OUT PLSA_TRANSLATED_NAME *Names,
+ IN LSAP_LOOKUP_LEVEL LookupLevel,
+ IN OUT PULONG MappedCount
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the internal client side version of the LsaLookupSids
+ API. It is called both from the client side of the Lsa and also
+ the server side of the LSA (when calling out to another LSA). The
+ function is identical to the LsaLookupSids API except that there is an
+ additional parameter, the LookupLevel parameter.
+
+ The LsaLookupSids API attempts to find names corresponding to Sids.
+ If a name can not be mapped to a Sid, the Sid is converted to character
+ form. The caller must have POLICY_LOOKUP_NAMES access to the Policy
+ object.
+
+ WARNING: This routine allocates memory for its output. The caller is
+ responsible for freeing this memory after use. See description of the
+ Names parameter.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ Count - Specifies the number of Sids to be translated.
+
+ Sids - Pointer to an array of Count pointers to Sids to be mapped
+ to names. The Sids may be well_known SIDs, SIDs of User accounts
+ Group Accounts, Alias accounts, or Domains.
+
+ ReferencedDomains - Receives a pointer to a structure describing the
+ domains used for the translation. The entries in this structure
+ are referenced by the strutcure returned via the Names parameter.
+ Unlike the Names paraemeter, which contains an array entry
+ for (each translated name, this strutcure will only contain
+ component for each domain utilized in the translation.
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ Names - Receives a pointer to array records describing each translated
+ name. The nth entry in this array provides a translation for
+ the nth entry in the Sids parameter.
+
+ All of the retruned names will be isolated names or NULL strings
+ (domain names are returned as NULL strings). If the caller needs
+ composite names, they can be generated by prepending the
+ isolated name with the domain name and a backslash. For example,
+ if (the name Sally is returned, and it is from the domain Manufact,
+ then the composite name would be "Manufact" + "\" + "Sally" or
+ "Manufact\Sally".
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ If a Sid is not translatable, then the following will occur:
+
+ 1) If the SID's domain is known, then a reference domain record
+ will be generated with the domain's name. In this case, the
+ name returned via the Names parameter is a Unicode representation
+ of the relative ID of the account, such as "(314)" or the null
+ string, if the Sid is that of a domain. So, you might end up
+ with a resultant name of "Manufact\(314) for the example with
+ Sally above, if Sally's relative id is 314.
+
+ 2) If not even the SID's domain could be located, then a full
+ Unicode representation of the SID is generated and no domain
+ record is referenced. In this case, the returned string might
+ be something like: "(S-1-672194-21-314)".
+
+ When this information is no longer needed, it must be released
+ by passing the returned pointer to LsaFreeMemory().
+
+ LookupLevel - Specifies the Level of Lookup to be performed on the
+ target machine. Values of this field are are follows:
+
+ LsapLookupWksta - First Level Lookup performed on a workstation
+ normally configured for Windows-Nt. The lookup searches the
+ Well-Known Sids, and the Built-in Domain and Account Domain
+ in the local SAM Database. If not all Sids are
+ identified, performs a "handoff" of a Second level Lookup to the
+ LSA running on a Controller for the workstation's Primary Domain
+ (if any).
+
+ LsapLookupPDC - Second Level Lookup performed on a Primary Domain
+ Controller. The lookup searches the Account Domain of the
+ SAM Database on the controller. If not all Sids are
+ found, the Trusted Domain List (TDL) is obtained from the
+ LSA's Policy Database and Third Level lookups are performed
+ via "handoff" to each Trusted Domain in the List.
+
+ LsapLookupTDL - Third Level Lookup performed on a controller
+ for a Trusted Domain. The lookup searches the Account Domain of
+ the SAM Database on the controller only.
+
+ MappedCount - Pointer to location that contains a count of the Sids
+ mapped so far. On exit, this count will be updated.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_SOME_NOT_MAPPED - Some or all of the names provided could not be
+ mapped. This is a warning only.
+
+ STATUS_TOO_MANY_SIDS - Too many Sids have been specified.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ BOOLEAN NamesArraySpecified = FALSE;
+
+ LSAPR_SID_ENUM_BUFFER SidEnumBuffer;
+
+ LSAPR_TRANSLATED_NAMES ReturnedNames;
+
+ //
+ // Verify that the Count is positive and not too high
+ //
+
+ if (Count == 0) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (Count > LSAP_DB_TRIAL_MAXIMUM_SID_COUNT) {
+
+ return STATUS_TOO_MANY_SIDS;
+ }
+
+ SidEnumBuffer.Entries = Count;
+ SidEnumBuffer.SidInfo = (PLSAPR_SID_INFORMATION) Sids;
+
+ RpcTryExcept {
+
+ //
+ // If this is a Workstation-Level lookup, the Names and
+ // ReferencedDomain Lists have not been created. Since these
+ // are input parameters in the general case, we need to set them
+ // to NULL.
+ //
+
+ if (LookupLevel == LsapLookupWksta) {
+
+ *ReferencedDomains = NULL;
+ *Names = NULL;
+ }
+
+ //
+ // There may already be a name translation array in cases where
+ // we are called internally (i.e. with lookup level higher than
+ // LsapLookupWksta). Initialize the ReturnedNames structure
+ // accordingly.
+ //
+
+ ReturnedNames.Entries = 0;
+ ReturnedNames.Names = NULL;
+
+ if (*Names != NULL) {
+
+ ReturnedNames.Entries = Count;
+ ReturnedNames.Names = (PLSAPR_TRANSLATED_NAME) *Names;
+ NamesArraySpecified = TRUE;
+ }
+
+ //
+ // Lookup Sids on the Server..
+ //
+
+ Status = LsarLookupSids(
+ (LSAPR_HANDLE) PolicyHandle,
+ &SidEnumBuffer,
+ (PLSAPR_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
+ &ReturnedNames,
+ LookupLevel,
+ MappedCount
+ );
+
+ //
+ // Return the array of translation to name info or NULL.
+ //
+ // NOTE: The array of name translations is allocated by the called
+ // client stub as a single block via MIDL_user_allocate, because
+ // Information is allocated all-nodes. We can therefore pass back the pointer
+ // directly to the client, who will be able to free the memory after
+ // use via LsaFreeMemory() [which makes a MIDL_user_free call].
+ //
+
+ *Names = (PLSA_TRANSLATED_NAME) ReturnedNames.Names;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the name translation array,
+ // free it.
+ //
+
+ if ((!NamesArraySpecified) && ReturnedNames.Names != NULL) {
+
+ MIDL_user_free( ReturnedNames.Names );
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaOpenAccount(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE AccountHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaOpenAccount API opens an account object in the Lsa Database of the
+ target system. An account must be opened before any operation can be
+ performed, including deletion of the account. A handle to the account
+ object is returned for use on subsequent API calls that access the
+ account. Before calling this API, the caller must have connected to
+ the target system's LSA and opened the Policy object by means
+ of a preceding call to LsaOpenPolicy.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenLsa call.
+
+ AccountSid - Pointer to the account's Sid.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the LSA Subsystem's LSA Database. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target Account object to determine whether the accesses will be
+ granted or denied.
+
+ AccountHandle - Pointer to location in which a handle to the opened
+ account object will be returned if the call succeeds.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_ACCOUNT_DOES_NOT_EXIST - There is no account object in the
+ target system's LSA Database having the specified AccountSid.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ Status = LsarOpenAccount(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) AccountSid,
+ DesiredAccess,
+ (PLSAPR_HANDLE) AccountHandle
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaEnumeratePrivilegesOfAccount(
+ IN LSA_HANDLE AccountHandle,
+ OUT PPRIVILEGE_SET *Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaEnumeratePrivilegesOfAccount API obtains information which
+ describes the privileges assigned to an account. This call requires
+ LSA_ACCOUNT_VIEW access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose privilege
+ information is to be obtained. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ Privileges - Receives a pointer to a buffer containing the Privilege
+ Set. The Privilege Set is an array of structures, one for each
+ privilege. Each structure contains the LUID of the privilege and
+ a mask of the privilege's attributes.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ *Privileges = NULL;
+
+ Status = LsarEnumeratePrivilegesAccount(
+ (LSAPR_HANDLE) AccountHandle,
+ (PLSAPR_PRIVILEGE_SET *) Privileges
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaAddPrivilegesToAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN PPRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaAddPrivilegesToAccount API adds privileges and their attributes
+ to an account object. If any provided privilege is already assigned
+ to the account object, the attributes of that privilege are replaced
+ by the newly rpovided values. This API call requires
+ LSA_ACCOUNT_ADJUST_PRIVILEGES access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object to which
+ privileges are to be added. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ Privileges - Points to a set of privileges (and their attributes) to
+ be assigned to the account.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ Status = LsarAddPrivilegesToAccount(
+ (LSAPR_HANDLE) AccountHandle,
+ (PLSAPR_PRIVILEGE_SET) Privileges
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaRemovePrivilegesFromAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN BOOLEAN AllPrivileges,
+ IN OPTIONAL PPRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaRemovePrivilegesFromAccount API removes privileges from an
+ account object. This API call requires LSA_ACCOUNT_ADJUST_PRIVILEGES
+ access to the account object. Note that if all privileges are removed
+ from the account object, the account object remains in existence until
+ deleted explicitly via a call to the LsaDelete API.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object to which
+ privileges are to be removed. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ AllPrivileges - If TRUE, then all privileges are to be removed from
+ the account. In this case, the Privileges parameter must be
+ specified as NULL. If FALSE, the Privileges parameter specifies
+ the privileges to be removed, and must be non NULL.
+
+ Privileges - Optionally points to a set of privileges (and their
+ attributes) to be removed from the account object. The attributes
+ fields of this structure are ignored. This parameter must
+ be specified as non-NULL if and only if AllPrivileges is set to
+ FALSE.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+
+ STATUS_INVALID_PARAMETER - The optional Privileges paraemter was
+ specified as NULL and AllPrivileges was set to FALSE.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ Status = LsarRemovePrivilegesFromAccount(
+ (LSAPR_HANDLE) AccountHandle,
+ AllPrivileges,
+ (PLSAPR_PRIVILEGE_SET) Privileges
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaGetQuotasForAccount(
+ IN LSA_HANDLE AccountHandle,
+ OUT PQUOTA_LIMITS QuotaLimits
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaGetQuotasForAccount API obtains the quota limits for pageable and
+ non-pageable memory (in Kilobytes) and the maximum execution time (in
+ seconds) for any session logged on to the account specified by
+ AccountHandle. For each quota and explicit value is returned. This
+ call requires LSA_ACCOUNT_VIEW access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose quotas
+ are to be obtained. This handle will have been returned
+ from a prior LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ QuotaLimits - Pointer to structure in which the system resource
+ quota limits applicable to each session logged on to this account
+ will be returned. Note that all quotas, including those specified
+ as being the system default values, are returned as actual values.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept{
+
+ Status = LsarGetQuotasForAccount(
+ (LSAPR_HANDLE) AccountHandle,
+ QuotaLimits
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaSetQuotasForAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN PQUOTA_LIMITS QuotaLimits
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetQuotasForAccount API sets the quota limits for pageable and
+ non-pageable memory (in Kilobytes) and the maximum execution time (in
+ seconds) for any session logged on to the account specified by
+ AccountHandle. For each quota an explicit value or the system default
+ may be specified. This call requires LSA_ACCOUNT_ADJUST_QUOTAS
+ access to the account object.
+
+Arguments:
+
+ AccountHandle - The handle to the open account object whose quotas
+ are to be set. This handle will have been returned from a prior
+ LsaOpenAccount or LsaCreateAccountInLsa API call.
+
+ QuotaLimits - Pointer to structure containing the system resource
+ quota limits applicable to each session logged on to this account.
+ A zero value specified in any field indicates that the current
+ System Default Quota Limit is to be applied.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is not valid.
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ Status = LsarSetQuotasForAccount(
+ (LSAPR_HANDLE) AccountHandle,
+ QuotaLimits
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsaGetSystemAccessAccount(
+ IN LSA_HANDLE AccountHandle,
+ OUT PULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaGetSystemAccessAccount() service returns the System Access
+ account flags for an Account object.
+
+Arguments:
+
+ AccountHandle - The handle to the Account object whose system access
+ flags are to be read. This handle will have been returned
+ from a preceding LsaOpenAccount() or LsaCreateAccount() call
+ an must be open for ACCOUNT_VIEW access.
+
+ SystemAccess - Points to location that will receive the system access
+ flags for the account.
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call was successful.
+
+ STATUS_ACCESS_DENIED - The AccountHandle does not specify
+ ACCOUNT_VIEW access.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is invalid.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Avoid RPC stub code raising exception on NULL handle so that
+ // we can return the error code STATUS_INVALID_HANDLE in this case
+ // too.
+ //
+
+ if (!ARGUMENT_PRESENT(AccountHandle)) {
+
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ RpcTryExcept{
+
+ Status = LsarGetSystemAccessAccount(
+ (LSAPR_HANDLE) AccountHandle,
+ SystemAccess
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaSetSystemAccessAccount(
+ IN LSA_HANDLE AccountHandle,
+ IN ULONG SystemAccess
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetSystemAccessAccount() service sets the System Access
+ account flags for an Account object.
+
+Arguments:
+
+ AccountHandle - The handle to the Account object whose system access
+ flags are to be read. This handle will have been returned
+ from a preceding LsaOpenAccount() or LsaCreateAccount() call
+ an must be open for ACCOUNT_ADJUST_SYSTEM_ACCESS access.
+
+ SystemAccess - A mask of the system access flags to assign to the
+ Account object. The valid access flags include:
+
+ POLICY_MODE_INTERACTIVE - Account can be accessed interactively
+
+ POLICY_MODE_NETWORK - Account can be accessed remotely
+
+ POLICY_MODE_SERVICE - TBS
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_SUCCESS - The call was successful.
+
+ STATUS_ACCESS_DENIED - The AccountHandle does not specify
+ ACCOUNT_VIEW access.
+
+ STATUS_INVALID_HANDLE - The specified AccountHandle is invalid.
+
+ STATUS_INVALID_PARAMETER - The specified Access Flags are invalid.
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // Avoid RPC stub code raising exception on NULL handle so that
+ // we can return the error code STATUS_INVALID_HANDLE in this case
+ // too.
+ //
+
+ if (!ARGUMENT_PRESENT(AccountHandle)) {
+
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ RpcTryExcept {
+
+ Status = LsarSetSystemAccessAccount(
+ (LSAPR_HANDLE) AccountHandle,
+ SystemAccess
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaFreeMemory(
+ IN PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+
+ Some LSA services that return a potentially large amount of memory,
+ such as an enumeration might, allocate the buffer in which the data
+ is returned. This function is used to free those buffers when they
+ are no longer needed.
+
+Parameters:
+
+ Buffer - Pointer to the buffer to be freed. This buffer must
+ have been allocated by a previous LSA service call.
+
+Return Values:
+
+ STATUS_SUCCESS - normal, successful completion.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ RpcTryExcept {
+
+ MIDL_user_free( Buffer );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return Status;
+}
+
+
+NTSTATUS
+LsapApiReturnResult(
+ ULONG ExceptionCode
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts an exception code or status value returned
+ from the client stub to a value suitable for return by the API to
+ the client.
+
+Arguments:
+
+ ExceptionCode - The exception code to be converted.
+
+Return Value:
+
+ NTSTATUS - The converted Nt Status code.
+
+--*/
+
+{
+ //
+ // Return the actual value if compatible with Nt status codes,
+ // otherwise, return STATUS_UNSUCCESSFUL.
+ //
+
+ if (!NT_SUCCESS((NTSTATUS) ExceptionCode)) {
+
+ return (NTSTATUS) ExceptionCode;
+
+ } else {
+
+ return STATUS_UNSUCCESSFUL;
+ }
+}
+
+
+NTSTATUS
+LsaOpenSecret(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING SecretName,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PLSA_HANDLE SecretHandle
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaOpenSecret API opens a Secret Object within the LSA Database.
+ A handle is returned which must be used to perform operations on the
+ secret object.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenLsa call.
+
+ SecretName - Pointer to a Unicode String structure that references the
+ name of the Secret object to be opened.
+
+ DesiredAccess - This is an access mask indicating accesses being
+ requested for the secret object being opened. These access types
+ are reconciled with the Discretionary Access Control List of the
+ target secret object to determine whether the accesses will be
+ granted or denied.
+
+
+ SecretHandle - Pointer to location that will receive a handle to the
+ newly opened Secret object.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
+ target system's LSA Database having the specified SecretName.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+ Status = LsarOpenSecret(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_UNICODE_STRING) SecretName,
+ DesiredAccess,
+ (PLSAPR_HANDLE) SecretHandle
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ return(Status);
+}
+
+
+NTSTATUS
+LsaSetSecret(
+ IN LSA_HANDLE SecretHandle,
+ IN OPTIONAL PUNICODE_STRING CurrentValue,
+ IN OPTIONAL PUNICODE_STRING OldValue
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaSetSecret API optionally sets one or both values associated with
+ a secret. These values are known as the "current value" and "old value"
+ of the secret and have a meaning known to the creator of the Secret
+ object. The values given are stored in encrypted form.
+
+Arguments:
+
+ SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
+
+ CurrentValue - Optional pointer to Unicode String containing the
+ value to be assigned as the "current value" of the Secret
+ object. The meaning of "current value" is dependent on the
+ purpose for which the Secret object is being used.
+
+ OldValue - Optional pointer to Unicode String containing the
+ value to be assigned as the "old value" of the Secret object.
+ The meaning of "old value" is dependent on the purpose for
+ which the Secret object is being used.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
+ target system's LSA Database having the specified SecretName.
+--*/
+
+{
+ NTSTATUS Status;
+
+ PLSAP_CR_CIPHER_VALUE CipherCurrentValue = NULL;
+ PLSAP_CR_CIPHER_VALUE CipherOldValue = NULL;
+ LSAP_CR_CLEAR_VALUE ClearCurrentValue;
+ LSAP_CR_CLEAR_VALUE ClearOldValue;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+
+ //
+ // Convert input from Unicode Structures to Clear Value Structures.
+ //
+
+ LsapCrUnicodeToClearValue( CurrentValue, &ClearCurrentValue );
+ LsapCrUnicodeToClearValue( OldValue, &ClearOldValue );
+
+ //
+ // Obtain the Session Key to be used to two-way encrypt the
+ // Current Value and/or Old Values.
+ //
+
+ RpcTryExcept {
+
+ Status = LsapCrClientGetSessionKey( SecretHandle, &SessionKey );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+ //
+ // Encrypt the Current Value if specified and not too long.
+ //
+
+ if (ARGUMENT_PRESENT(CurrentValue)) {
+
+ Status = LsapCrEncryptValue(
+ &ClearCurrentValue,
+ SessionKey,
+ &CipherCurrentValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+ }
+
+ //
+ // Encrypt the Old Value if specified and not too long.
+ //
+
+ if (ARGUMENT_PRESENT(OldValue)) {
+
+ Status = LsapCrEncryptValue(
+ (PLSAP_CR_CLEAR_VALUE) &ClearOldValue,
+ SessionKey,
+ &CipherOldValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+ }
+
+ //
+ // Set the Secret Values.
+ //
+
+ RpcTryExcept {
+
+ Status = LsarSetSecret(
+ (LSAPR_HANDLE) SecretHandle,
+ (PLSAPR_CR_CIPHER_VALUE) CipherCurrentValue,
+ (PLSAPR_CR_CIPHER_VALUE) CipherOldValue
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto SetSecretError;
+ }
+
+SetSecretFinish:
+
+ //
+ // If necessary, free memory allocated for the Encrypted Current Value.
+ //
+
+ if (CipherCurrentValue != NULL) {
+
+ LsaFreeMemory(CipherCurrentValue);
+ }
+
+ //
+ // If necessary, free memory allocated for the Encrypted Old Value.
+ //
+
+ if (CipherOldValue != NULL) {
+
+ LsaFreeMemory(CipherOldValue);
+ }
+
+ //
+ // If necessary, free memory allocated for the Session Key.
+ //
+
+ if (SessionKey != NULL) {
+
+ MIDL_user_free(SessionKey);
+ }
+
+ return(Status);
+
+SetSecretError:
+
+ goto SetSecretFinish;
+}
+
+
+NTSTATUS
+LsaQuerySecret(
+ IN LSA_HANDLE SecretHandle,
+ IN OUT OPTIONAL PUNICODE_STRING *CurrentValue,
+ OUT PLARGE_INTEGER CurrentValueSetTime,
+ IN OUT OPTIONAL PUNICODE_STRING *OldValue,
+ OUT PLARGE_INTEGER OldValueSetTime
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaQuerySecret API optionally returns one or both of the values
+ assigned to a Secret object. These values are known as the "current value"
+ and the "old value", and they have a meaning known to the creator of the
+ Secret object. The values are returned in their original unencrypted form.
+ The caller must have LSA_QUERY_SECRET access to the Secret object.
+
+Arguments:
+
+ SecretHandle - Handle from an LsaOpenSecret or LsaCreateSecret call.
+
+ CurrentValue - Optional pointer to location which will receive a pointer
+ to a Unicode String containing the value assigned as the "current
+ value" of the secret object. If no "current value" is assigned to
+ the Secret object, a NULL pointer is returned.
+
+ CurrentValueSetTime - The date/time at which the current secret value
+ was established.
+
+ OldValue - Optional pointer to location which will receive a pointer
+ to a Unicode String containing the value assigned as the "old
+ value" of the secret object. If no "current value" is assigned to
+ the Secret object, a NULL pointer is returned.
+
+ OldValueSetTime - The date/time at which the old secret value
+ was established.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - There is no Secret object in the
+ target system's LSA Database having the specified SecretName.
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PLSAP_CR_CIPHER_VALUE CipherCurrentValue = NULL;
+ PLSAP_CR_CIPHER_VALUE CipherOldValue = NULL;
+ PLSAP_CR_CLEAR_VALUE ClearCurrentValue = NULL;
+ PLSAP_CR_CLEAR_VALUE ClearOldValue = NULL;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+
+ RpcTryExcept {
+
+ Status = LsarQuerySecret(
+ (PLSAPR_HANDLE) SecretHandle,
+ (PLSAPR_CR_CIPHER_VALUE *) &CipherCurrentValue,
+ CurrentValueSetTime,
+ (PLSAPR_CR_CIPHER_VALUE *) &CipherOldValue,
+ OldValueSetTime
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ //
+ // Obtain the Session Key to be used to two-way encrypt the
+ // Current Value and/or Old Values.
+ //
+
+ RpcTryExcept {
+
+ Status = LsapCrClientGetSessionKey( SecretHandle, &SessionKey );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ //
+ // If the Current Value is requested and a Current Value exists,
+ // decrypt it using the Session key. Otherwise store NULL for return.
+ //
+
+ if (ARGUMENT_PRESENT(CurrentValue)) {
+
+ if (CipherCurrentValue != NULL) {
+
+ Status = LsapCrDecryptValue(
+ CipherCurrentValue,
+ SessionKey,
+ &ClearCurrentValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ //
+ // Convert Clear Current Value to Unicode
+ //
+
+ LsapCrClearValueToUnicode(
+ ClearCurrentValue,
+ (PUNICODE_STRING) ClearCurrentValue
+ );
+ *CurrentValue = (PUNICODE_STRING) ClearCurrentValue;
+
+ } else {
+
+ *CurrentValue = NULL;
+ }
+ }
+
+ //
+ // If the Old Value is requested and an Old Value exists,
+ // decrypt it using the Session key. Otherwise store NULL for return.
+ //
+
+ if (ARGUMENT_PRESENT(OldValue)) {
+
+ if (CipherOldValue != NULL) {
+
+ Status = LsapCrDecryptValue(
+ CipherOldValue,
+ SessionKey,
+ &ClearOldValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ //
+ // Convert Clear Old Value to Unicode
+ //
+
+ LsapCrClearValueToUnicode(
+ ClearOldValue,
+ (PUNICODE_STRING) ClearOldValue
+ );
+
+ *OldValue = (PUNICODE_STRING) ClearOldValue;
+
+ } else {
+
+ *OldValue = NULL;
+ }
+ }
+
+QuerySecretFinish:
+
+ //
+ // If necessary, free memory allocated for the Session Key.
+ //
+
+ if (SessionKey != NULL) {
+
+ MIDL_user_free(SessionKey);
+ }
+
+ //
+ // If necessary, free memory allocated for the returned Encrypted
+ // Current Value.
+ //
+
+ if (CipherCurrentValue != NULL) {
+
+ LsapCrFreeMemoryValue(CipherCurrentValue);
+ }
+
+ //
+ // If necessary, free memory allocated for the returned Encrypted
+ // Old Value.
+ //
+
+ if (CipherOldValue != NULL) {
+
+ LsapCrFreeMemoryValue(CipherOldValue);
+ }
+
+ return(Status);
+
+QuerySecretError:
+
+ //
+ // If necessary, free memory allocated for the Clear Current Value
+ //
+
+ if (ClearCurrentValue != NULL) {
+
+ LsapCrFreeMemoryValue(ClearCurrentValue);
+ }
+
+ //
+ // If necessary, free memory allocated for the Clear Old Value
+ // Unicode string (buffer and structure).
+ //
+
+ if (ClearOldValue != NULL) {
+
+ LsapCrFreeMemoryValue(ClearOldValue);
+ }
+
+
+ if (ARGUMENT_PRESENT(CurrentValue)) {
+
+ *CurrentValue = NULL;
+ }
+
+ if (ARGUMENT_PRESENT(OldValue)) {
+
+ *OldValue = NULL;
+ }
+
+ goto QuerySecretFinish;
+}
+
+
+NTSTATUS
+LsaGetUserName(
+ OUT PUNICODE_STRING * UserName,
+ OUT OPTIONAL PUNICODE_STRING * DomainName
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the callers user name and domain name
+
+
+Arguments:
+
+ UserName - Receives a pointer to the user's name.
+
+ DomainName - Optionally receives a pointer to the user's domain name.
+
+
+Return Value:
+
+ NTSTATUS - The privilege was found and returned.
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PLSAPR_UNICODE_STRING UserNameBuffer = NULL;
+ PLSAPR_UNICODE_STRING DomainNameBuffer = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaGetUserName
+ //
+
+ Status = LsarGetUserName(
+ NULL,
+ &UserNameBuffer,
+ ARGUMENT_PRESENT(DomainName) ? &DomainNameBuffer : NULL
+ );
+
+ (*UserName) = (PUNICODE_STRING)UserNameBuffer;
+
+ if (ARGUMENT_PRESENT(DomainName)) {
+ (*DomainName) = (PUNICODE_STRING)DomainNameBuffer;
+ }
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the return buffer, free it.
+ //
+
+ if (UserNameBuffer != NULL) {
+
+ MIDL_user_free(UserNameBuffer);
+ }
+
+ if (DomainNameBuffer != NULL) {
+
+ MIDL_user_free(DomainNameBuffer);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+
+ return(Status);
+
+
+}
+
+
diff --git a/private/lsa/uclient/rpcapi2.c b/private/lsa/uclient/rpcapi2.c
new file mode 100644
index 000000000..25fb11ee0
--- /dev/null
+++ b/private/lsa/uclient/rpcapi2.c
@@ -0,0 +1,3316 @@
+
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rpcapi.c
+
+Abstract:
+
+ This module contains the routines for the LSA API that use RPC. The
+ routines in this module are merely wrappers that work as follows:
+
+ o Client program calls LsaFoo in this module
+ o LsaFoo calls RPC client stub interface routine LsapFoo with
+ similar parameters. Some parameters are translated from types
+ (e.g structures containing PVOIDs or certain kinds of variable length
+ parameters such as pointers to SID's) that are not specifiable on an
+ RPC interface, to specifiable form.
+ o RPC client stub LsapFoo calls interface specific marshalling routines
+ and RPC runtime to marshal parameters into a buffer and send them over
+ to the server side of the LSA.
+ o Server side calls RPC runtime and interface specific unmarshalling
+ routines to unmarshal parameters.
+ o Server side calls worker LsapFoo to perform API function.
+ o Server side marshals response/output parameters and communicates these
+ back to client stub LsapFoo
+ o LsapFoo exits back to LsaFoo which returns to client program.
+
+Author:
+
+ Mike Swift (MikeSw) December 7, 1994
+
+Revision History:
+
+--*/
+
+#define UNICODE // required for TEXT() to be defined properly
+#include "lsaclip.h"
+
+#include <lmcons.h>
+#include <logonmsv.h>
+
+
+typedef struct _LSAP_DB_RIGHT_AND_ACCESS {
+ UNICODE_STRING UserRight;
+ ULONG SystemAccess;
+} LSAP_DB_RIGHT_AND_ACCESS, *PLSAP_DB_RIGHT_AND_ACCESS;
+
+#define LSAP_DB_SYSTEM_ACCESS_TYPES 4
+
+LSAP_DB_RIGHT_AND_ACCESS LsapDbRightAndAccess[LSAP_DB_SYSTEM_ACCESS_TYPES] = {
+ {{sizeof(SE_INTERACTIVE_LOGON_NAME)-sizeof(WCHAR),
+ sizeof(SE_INTERACTIVE_LOGON_NAME),
+ SE_INTERACTIVE_LOGON_NAME},
+ SECURITY_ACCESS_INTERACTIVE_LOGON},
+ {{sizeof(SE_NETWORK_LOGON_NAME)-sizeof(WCHAR),
+ sizeof(SE_NETWORK_LOGON_NAME),
+ SE_NETWORK_LOGON_NAME},
+ SECURITY_ACCESS_NETWORK_LOGON},
+ {{sizeof(SE_BATCH_LOGON_NAME)-sizeof(WCHAR),
+ sizeof(SE_BATCH_LOGON_NAME),
+ SE_BATCH_LOGON_NAME},
+ SECURITY_ACCESS_BATCH_LOGON},
+ {{sizeof(SE_SERVICE_LOGON_NAME)-sizeof(WCHAR),
+ sizeof(SE_SERVICE_LOGON_NAME),
+ SE_SERVICE_LOGON_NAME},
+ SECURITY_ACCESS_SERVICE_LOGON}
+ };
+
+//
+// Structure to maintain list of enumerated accounts
+//
+
+typedef struct _SID_LIST_ENTRY {
+ struct _SID_LIST_ENTRY * Next;
+ PSID Sid;
+} SID_LIST_ENTRY, *PSID_LIST_ENTRY;
+
+//
+// Functions private to this module
+//
+
+NTSTATUS
+LsapApiReturnResult(
+ IN ULONG ExceptionCode
+ );
+
+NTSTATUS
+LsapApiConvertRightsToPrivileges(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING UserRights,
+ IN ULONG RightCount,
+ OUT PPRIVILEGE_SET * Privileges,
+ OUT PULONG SystemAccess
+ );
+
+NTSTATUS
+LsapApiConvertPrivilegesToRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN OPTIONAL PPRIVILEGE_SET Privileges,
+ IN OPTIONAL ULONG SystemAccess,
+ OUT PUNICODE_STRING * UserRights,
+ OUT PULONG RightCount
+ );
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// This set of routines implements the same functionality as the APIs
+// below but do it with the APIs present through NT 3.5
+//
+/////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+NTAPI
+LsapEnumerateAccountsWithUserRight(
+ IN LSA_HANDLE PolicyHandle,
+ IN OPTIONAL PUNICODE_STRING UserRights,
+ OUT PVOID *EnumerationBuffer,
+ OUT PULONG CountReturned
+ )
+
+/*++
+
+Routine Description:
+
+
+ The LsaEnumerateAccountsWithUserRight API returns information about
+ the accounts in the target system's Lsa Database. This call requires
+ LSA_ENUMERATE_ACCOUNTS access to the Policy object. Since this call
+ accesses the privileges of an account, you must have ACCOUNT_VIEW access
+ access to all accounts.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ UserRight - Name of the right that the account must have.
+
+ Buffer - Receives a pointer to a LSA_ENUMERATION_INFORMATION structure
+ containing the SIDs of all the accounts.
+
+ CountReturned - Receives the number of sids returned.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if no objects are enumerated because the
+ EnumerationContext value passed in is too high.
+--*/
+
+
+{
+ NTSTATUS Status;
+ PLSA_ENUMERATION_INFORMATION Accounts = NULL;
+ PPRIVILEGE_SET DesiredPrivilege = NULL;
+ ULONG DesiredAccess = 0;
+ PPRIVILEGE_SET Privileges = NULL;
+ ULONG SystemAccess;
+ LSA_ENUMERATION_HANDLE EnumContext = 0;
+ ULONG AccountCount;
+ ULONG AccountIndex;
+ LSA_HANDLE AccountHandle = NULL;
+ PSID_LIST_ENTRY AccountList = NULL;
+ PSID_LIST_ENTRY NextAccount = NULL;
+ ULONG AccountSize;
+ PUCHAR Where;
+ ULONG PrivilegeIndex;
+
+ Status = LsapApiConvertRightsToPrivileges(
+ PolicyHandle,
+ UserRights,
+ (UserRights ? 1 : 0),
+ &DesiredPrivilege,
+ &DesiredAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Enumerate all the accounts.
+ //
+
+ do
+ {
+ Status = LsaEnumerateAccounts(
+ PolicyHandle,
+ &EnumContext,
+ &Accounts,
+ 32000,
+ &AccountCount
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ //
+ // For each account, check that it has the desired right
+ //
+
+ for (AccountIndex = 0; AccountIndex < AccountCount ; AccountIndex++ ) {
+
+ if ((DesiredPrivilege != NULL) || (DesiredAccess != 0)) {
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ Accounts[AccountIndex].Sid,
+ ACCOUNT_VIEW,
+ &AccountHandle
+ );
+
+ if (!NT_SUCCESS(Status) ) {
+ goto Cleanup;
+ }
+
+ //
+ // If a privilege was requested, get the privilegs
+ //
+
+ if (DesiredPrivilege != NULL) {
+
+ Privileges = NULL;
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandle,
+ &Privileges
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Search for the desired privilege
+ //
+
+ for (PrivilegeIndex = 0;
+ PrivilegeIndex < Privileges->PrivilegeCount ;
+ PrivilegeIndex++) {
+
+ if (RtlEqualLuid(&Privileges->Privilege[PrivilegeIndex].Luid,
+ &DesiredPrivilege->Privilege[0].Luid)) {
+ break;
+ }
+ }
+
+ //
+ // If we found the privilege, add it to the list.
+ //
+
+ if (PrivilegeIndex != Privileges->PrivilegeCount) {
+
+ //
+ // Add this account to the enumeration.
+ //
+
+ NextAccount = MIDL_user_allocate(sizeof(SID_LIST_ENTRY));
+ if (NextAccount == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ NextAccount->Sid = MIDL_user_allocate(RtlLengthSid(Accounts[AccountIndex].Sid));
+ if (NextAccount->Sid == NULL) {
+ MIDL_user_free(NextAccount);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ RtlCopyMemory(
+ NextAccount->Sid,
+ Accounts[AccountIndex].Sid,
+ RtlLengthSid(Accounts[AccountIndex].Sid)
+ );
+ NextAccount->Next = AccountList;
+ AccountList = NextAccount;
+
+ }
+ LsaFreeMemory(Privileges);
+ Privileges = NULL;
+
+ } else {
+
+ //
+ // Otherwise get the system access
+ //
+
+ ASSERT(DesiredAccess != 0);
+
+ Status = LsaGetSystemAccessAccount(
+ AccountHandle,
+ &SystemAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Check for the desired access
+ //
+
+ if ((SystemAccess & DesiredAccess) != 0) {
+
+ //
+ // Add this account to the enumeration.
+ //
+
+ NextAccount = MIDL_user_allocate(sizeof(SID_LIST_ENTRY));
+ if (NextAccount == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ NextAccount->Sid = MIDL_user_allocate(RtlLengthSid(Accounts[AccountIndex].Sid));
+ if (NextAccount->Sid == NULL) {
+ MIDL_user_free(NextAccount);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ RtlCopyMemory(
+ NextAccount->Sid,
+ Accounts[AccountIndex].Sid,
+ RtlLengthSid(Accounts[AccountIndex].Sid)
+ );
+ NextAccount->Next = AccountList;
+ AccountList = NextAccount;
+
+ }
+ }
+
+ LsaClose(AccountHandle);
+ AccountHandle = NULL;
+
+
+ } else {
+ //
+ // always add the account if the caller didn't want
+ // filtering.
+ //
+
+ NextAccount = MIDL_user_allocate(sizeof(SID_LIST_ENTRY));
+ if (NextAccount == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ NextAccount->Sid = MIDL_user_allocate(RtlLengthSid(Accounts[AccountIndex].Sid));
+ if (NextAccount->Sid == NULL) {
+ MIDL_user_free(NextAccount);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+ RtlCopyMemory(
+ NextAccount->Sid,
+ Accounts[AccountIndex].Sid,
+ RtlLengthSid(Accounts[AccountIndex].Sid)
+ );
+ NextAccount->Next = AccountList;
+ AccountList = NextAccount;
+ }
+
+ }
+ LsaFreeMemory(Accounts);
+ Accounts = NULL;
+
+ } while ( 1 );
+
+ if (Status != STATUS_NO_MORE_ENTRIES) {
+ goto Cleanup;
+ }
+
+ AccountSize = 0;
+ AccountCount = 0;
+ for (NextAccount = AccountList ; NextAccount != NULL; NextAccount = NextAccount->Next) {
+ AccountSize += sizeof(LSA_ENUMERATION_INFORMATION) +
+ RtlLengthSid(NextAccount->Sid);
+ AccountCount++;
+ }
+
+ //
+ // If there were no accounts return a warning now.
+ //
+
+ if (AccountCount == 0) {
+ *EnumerationBuffer = NULL;
+ *CountReturned = 0;
+ Status = STATUS_NO_MORE_ENTRIES;
+ goto Cleanup;
+ }
+
+ Accounts = MIDL_user_allocate(AccountSize);
+ if (Accounts == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ //
+ // Marshall all the sids into the array.
+ //
+
+ Where = (PUCHAR) Accounts + AccountCount * sizeof(LSA_ENUMERATION_INFORMATION);
+
+ for ( NextAccount = AccountList,AccountIndex = 0 ;
+ NextAccount != NULL;
+ NextAccount = NextAccount->Next, AccountIndex++) {
+
+ Accounts[AccountIndex].Sid = (PSID) Where;
+ RtlCopyMemory(
+ Where,
+ NextAccount->Sid,
+ RtlLengthSid(NextAccount->Sid)
+ );
+ Where += RtlLengthSid(NextAccount->Sid);
+ }
+ ASSERT(AccountIndex == AccountCount);
+ ASSERT(Where - (PUCHAR) Accounts == (LONG) AccountSize);
+ *EnumerationBuffer = Accounts;
+ Accounts = NULL;
+ *CountReturned = AccountCount;
+ Status = STATUS_SUCCESS;
+
+
+Cleanup:
+ if (AccountList != NULL) {
+ while (AccountList != NULL) {
+ NextAccount = AccountList->Next;
+ MIDL_user_free(AccountList->Sid);
+ MIDL_user_free(AccountList);
+ AccountList = NextAccount;
+ }
+ }
+
+ if (Accounts != NULL) {
+ MIDL_user_free(Accounts);
+ }
+
+ if (Privileges != NULL) {
+ LsaFreeMemory(Privileges);
+ }
+ return(Status);
+}
+
+
+
+NTSTATUS
+NTAPI
+LsapEnumerateAccountRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ OUT PUNICODE_STRING *UserRights,
+ OUT PULONG CountOfRights
+ )
+
+/*++
+
+Routine Description:
+
+ Returns all the rights of an account. This is done by gathering the
+ privileges and system access of an account and translating that into
+ an array of strings.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall. This API requires
+ no special access.
+
+ AccountSid - Sid of account to open.
+
+ UserRights - receives an array of user rights (UNICODE_STRING) for
+ the account.
+
+ CountOfRights - receives the number of rights returned.
+
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - the caller did not have sufficient access to
+ return the privileges or system access of the account.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - the specified account did not exist.
+
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the
+ request.
+
+--*/
+{
+ NTSTATUS Status;
+ PPRIVILEGE_SET Privileges = NULL;
+ ULONG SystemAccess = 0;
+ PUNICODE_STRING Rights = NULL;
+ ULONG RightCount = 0;
+ LSA_HANDLE AccountHandle = NULL;
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ AccountSid,
+ ACCOUNT_VIEW,
+ &AccountHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Query the privilegs and system access
+ //
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandle,
+ &Privileges
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaGetSystemAccessAccount(
+ AccountHandle,
+ &SystemAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Convert the privileges and access to rights
+ //
+
+ Status = LsapApiConvertPrivilegesToRights(
+ PolicyHandle,
+ Privileges,
+ SystemAccess,
+ &Rights,
+ &RightCount
+ );
+ if (NT_SUCCESS(Status)) {
+ *CountOfRights = RightCount;
+ *UserRights = Rights;
+ }
+Cleanup:
+ if (Privileges != NULL) {
+ LsaFreeMemory(Privileges);
+ }
+ if (AccountHandle != NULL) {
+ LsaClose(AccountHandle);
+ }
+
+ return(Status);
+
+}
+
+NTSTATUS
+NTAPI
+LsapAddAccountRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ IN PUNICODE_STRING UserRights,
+ IN ULONG CountOfRights
+ )
+/*++
+
+Routine Description:
+
+ Adds rights to the account specified by the account sid. If the account
+ does not exist, it creates the account.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call. The handle must have
+ POLICY_CREATE_ACCOUNT access if this is the first call for this
+ AccountSid.
+
+ AccountSid - Sid of account to add rights to
+
+ UserRights - Array of unicode strings naming rights to add to the
+ account.
+
+Return Value:
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
+
+ STATUS_INVALID_PARAMTER - one of the parameters was not present
+
+ STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
+
+ STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
+ account to add privileges.
+
+--*/
+{
+ LSA_HANDLE AccountHandle = NULL;
+ NTSTATUS Status;
+ PPRIVILEGE_SET Privileges = NULL;
+ ULONG SystemAccess;
+ ULONG OldAccess;
+
+ //
+ // Convert the rights into privileges and system access.
+ //
+
+ Status = LsapApiConvertRightsToPrivileges(
+ PolicyHandle,
+ UserRights,
+ CountOfRights,
+ &Privileges,
+ &SystemAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Open the account. If it does not exist ,create the account.
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ AccountSid,
+ ACCOUNT_ADJUST_PRIVILEGES |
+ ACCOUNT_ADJUST_SYSTEM_ACCESS |
+ ACCOUNT_VIEW,
+ &AccountHandle
+ );
+
+ //
+ // if the account did not exist, try to create it.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = LsaCreateAccount(
+ PolicyHandle,
+ AccountSid,
+ ACCOUNT_ADJUST_PRIVILEGES |
+ ACCOUNT_ADJUST_SYSTEM_ACCESS |
+ ACCOUNT_VIEW,
+ &AccountHandle
+ );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaGetSystemAccessAccount(
+ AccountHandle,
+ &OldAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaSetSystemAccessAccount(
+ AccountHandle,
+ OldAccess | SystemAccess
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaAddPrivilegesToAccount(
+ AccountHandle,
+ Privileges
+ );
+Cleanup:
+
+ if (Privileges != NULL) {
+ MIDL_user_free(Privileges);
+ }
+ if (AccountHandle != NULL) {
+ LsaClose(AccountHandle);
+ }
+ return(Status);
+}
+
+NTSTATUS
+NTAPI
+LsapRemoveAccountRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ IN BOOLEAN AllRights,
+ IN PUNICODE_STRING UserRights,
+ IN ULONG CountOfRights
+ )
+
+/*++
+
+Routine Description:
+
+ Removes rights to the account specified by the account sid. If the
+ AllRights flag is set or if all the rights are removed, the account
+ is deleted.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call
+
+ AccountSid - Sid of account to remove rights from
+
+ UserRights - Array of unicode strings naming rights to remove from the
+ account.
+
+Return Value:
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
+
+ STATUS_INVALID_PARAMTER - one of the parameters was not present
+
+ STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
+
+ STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
+ account to add privileges.
+
+--*/
+{
+ LSA_HANDLE AccountHandle = NULL;
+ NTSTATUS Status;
+ PPRIVILEGE_SET Privileges = NULL;
+ PPRIVILEGE_SET NewPrivileges = NULL;
+ ULONG SystemAccess;
+ ULONG OldAccess;
+ ULONG DesiredAccess;
+ ULONG NewAccess;
+
+ //
+ // Convert the rights into privileges and system access.
+ //
+
+ if (!AllRights) {
+ Status = LsapApiConvertRightsToPrivileges(
+ PolicyHandle,
+ UserRights,
+ CountOfRights,
+ &Privileges,
+ &SystemAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ DesiredAccess = ACCOUNT_ADJUST_PRIVILEGES |
+ ACCOUNT_ADJUST_SYSTEM_ACCESS |
+ ACCOUNT_VIEW | DELETE;
+ } else {
+ DesiredAccess = DELETE;
+ }
+
+
+
+ //
+ // Open the account.
+ //
+
+ Status = LsaOpenAccount(
+ PolicyHandle,
+ AccountSid,
+ DesiredAccess,
+ &AccountHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // If we are to remove all rights, just delete the account ,and if that
+ // succeeds, zero the handle so we don't try to close it later.
+ //
+
+ if (AllRights) {
+ Status = LsaDelete(
+ AccountHandle
+ );
+ if (NT_SUCCESS(Status)) {
+ AccountHandle = NULL;
+ }
+ goto Cleanup;
+ }
+
+ //
+ // Get the old system access to adjust
+ //
+
+ Status = LsaGetSystemAccessAccount(
+ AccountHandle,
+ &OldAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+
+ NewAccess = OldAccess & ~SystemAccess;
+ Status = LsaSetSystemAccessAccount(
+ AccountHandle,
+ NewAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaRemovePrivilegesFromAccount(
+ AccountHandle,
+ FALSE, // don't remove all
+ Privileges
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Now query the privilegs to see if they are zero. If so, and
+ // system access is zero, delete the account.
+ //
+
+ Status = LsaEnumeratePrivilegesOfAccount(
+ AccountHandle,
+ &NewPrivileges
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // If the account has no privileges or access, delete it.
+ //
+
+ if ((NewPrivileges->PrivilegeCount == 0) &&
+ (NewAccess == 0)) {
+
+ Status = LsaDelete(
+ AccountHandle
+ );
+ if (NT_SUCCESS(Status)) {
+ AccountHandle = NULL;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if (Privileges != NULL) {
+ MIDL_user_free(Privileges);
+ }
+ if (AccountHandle != NULL) {
+ LsaClose(AccountHandle);
+ }
+ if (NewPrivileges != NULL) {
+ LsaFreeMemory(NewPrivileges);
+ }
+ return(Status);
+
+}
+
+
+NTSTATUS
+LsapApiBuildSecretName(
+ PTRUSTED_DOMAIN_NAME_INFO NameInfo,
+ PUNICODE_STRING OutputSecretName
+ )
+{
+ UNICODE_STRING SecretName;
+
+ //
+ // The secret name is G$$domain name, where G$ is the global prefix and
+ // $ is the ssi prefix
+ //
+
+ SecretName.Length = NameInfo->Name.Length +
+ (SSI_SECRET_PREFIX_LENGTH +
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH) * sizeof(WCHAR);
+ SecretName.MaximumLength = SecretName.Length;
+ SecretName.Buffer = (LPWSTR) MIDL_user_allocate( SecretName.Length );
+
+ if (SecretName.Buffer == NULL) {
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ wcscpy(
+ SecretName.Buffer,
+ LSA_GLOBAL_SECRET_PREFIX
+ );
+
+ wcscat(
+ SecretName.Buffer,
+ SSI_SECRET_PREFIX
+ );
+ RtlCopyMemory(
+ SecretName.Buffer +
+ LSA_GLOBAL_SECRET_PREFIX_LENGTH +
+ SSI_SECRET_PREFIX_LENGTH,
+ NameInfo->Name.Buffer,
+ NameInfo->Name.Length
+ );
+ *OutputSecretName = SecretName;
+ return(STATUS_SUCCESS);
+
+}
+
+NTSTATUS
+NTAPI
+LsapQueryTrustedDomainInfo(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ OUT PVOID *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaQueryTrustedDomainInfo API obtains information from a
+ TrustedDomain object. The caller must have access appropriate to the
+ information being requested (see InformationClass parameter). It also
+ may query the secret object (for the TrustedDomainPasswordInformation
+ class).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to query.
+
+ InformationClass - Specifies the information to be returned.
+
+ Buffer - Receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+{
+ NTSTATUS Status;
+ LSA_HANDLE DomainHandle = NULL;
+ LSA_HANDLE SecretHandle = NULL;
+ PUNICODE_STRING OldPassword = NULL;
+ PUNICODE_STRING Password = NULL;
+ PTRUSTED_PASSWORD_INFO PasswordInfo = NULL;
+ PTRUSTED_DOMAIN_NAME_INFO NameInfo = NULL;
+ ULONG DesiredAccess;
+ PVOID LocalBuffer = NULL;
+ TRUSTED_INFORMATION_CLASS LocalInfoClass;
+ UNICODE_STRING SecretName;
+ PUCHAR Where;
+ ULONG PasswordSize;
+
+ SecretName.Buffer = NULL;
+
+ //
+ // Find the desired access type for the info we are
+ // querying.
+ //
+
+ LocalInfoClass = InformationClass;
+
+ switch(InformationClass) {
+ case TrustedDomainNameInformation:
+ DesiredAccess = TRUSTED_QUERY_DOMAIN_NAME;
+ break;
+ case TrustedPosixOffsetInformation:
+ DesiredAccess = TRUSTED_QUERY_POSIX;
+ break;
+ case TrustedPasswordInformation:
+ DesiredAccess = TRUSTED_QUERY_DOMAIN_NAME;
+ LocalInfoClass = TrustedDomainNameInformation;
+ break;
+ default:
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Open the domain for the desired access
+ //
+
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ DesiredAccess,
+ &DomainHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaQueryInfoTrustedDomain(
+ DomainHandle,
+ LocalInfoClass,
+ &LocalBuffer
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // If the class wasn't trusted password information, return here.
+ //
+
+ if (InformationClass != TrustedPasswordInformation) {
+ *Buffer = LocalBuffer;
+ LocalBuffer = NULL;
+ goto Cleanup;
+ }
+ NameInfo = (PTRUSTED_DOMAIN_NAME_INFO) LocalBuffer;
+
+ //
+ // Get the secret name
+ //
+
+ Status = LsapApiBuildSecretName(
+ NameInfo,
+ &SecretName
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &SecretName,
+ SECRET_QUERY_VALUE,
+ &SecretHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Query the secret
+ //
+
+ Status = LsaQuerySecret(
+ SecretHandle,
+ &Password,
+ NULL,
+ &OldPassword,
+ NULL
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Marshall the passwords into the output structure.
+ //
+
+ PasswordSize = sizeof(TRUSTED_PASSWORD_INFO);
+ if (Password != NULL) {
+ PasswordSize += Password->MaximumLength;
+ }
+
+ if (OldPassword != NULL) {
+ PasswordSize += OldPassword->MaximumLength;
+ }
+
+ PasswordInfo = (PTRUSTED_PASSWORD_INFO) MIDL_user_allocate(PasswordSize);
+ if (PasswordInfo == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ RtlZeroMemory(
+ PasswordInfo,
+ PasswordSize
+ );
+
+ Where = (PUCHAR) (PasswordInfo+1);
+
+ if (Password != NULL) {
+ PasswordInfo->Password = *Password;
+ PasswordInfo->Password.Buffer = (LPWSTR) Where;
+ RtlCopyMemory(
+ Where,
+ Password->Buffer,
+ Password->MaximumLength
+ );
+ Where += Password->MaximumLength;
+ }
+
+ if (OldPassword != NULL) {
+ PasswordInfo->OldPassword = *OldPassword;
+ PasswordInfo->OldPassword.Buffer = (LPWSTR) Where;
+ RtlCopyMemory(
+ Where,
+ OldPassword->Buffer,
+ OldPassword->MaximumLength
+ );
+ Where += OldPassword->MaximumLength;
+ }
+
+ ASSERT(Where - (PUCHAR) PasswordInfo == (LONG) PasswordSize);
+
+ *Buffer = PasswordInfo;
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ if (DomainHandle != NULL) {
+ LsaClose(DomainHandle);
+ }
+
+ if (SecretHandle != NULL) {
+ LsaClose(SecretHandle);
+ }
+
+ if (LocalBuffer != NULL) {
+ LsaFreeMemory(LocalBuffer);
+ }
+
+ if (SecretName.Buffer != NULL) {
+ MIDL_user_free(SecretName.Buffer);
+ }
+
+ return(Status);
+
+}
+
+NTSTATUS
+NTAPI
+LsapSetTrustedDomainInformation(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID Buffer
+ )
+/*++
+
+Routine Description:
+
+
+ The LsaSetTrustedDomainInformation API modifies information in the Trusted
+ Domain Object and in the Secret Object. The caller must have access
+ appropriate to the information to be changed in the Policy Object, see
+ the InformationClass parameter.
+
+ If the domain does not yet exist and the information class is
+ TrustedDomainNameInformation, then the domain is created. If the
+ domain exists and the class is TrustedDomainNameInformation, an
+ error is returned.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to modify.
+
+ InformationClass - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ TrustedDomainNameInformation POLICY_TRUST_ADMIN
+ TrustedPosixOffsetInformation none
+ TrustedPasswordInformation POLICY_CREATE_SECRET
+
+ Buffer - Points to a structure containing the information appropriate
+ to the InformationClass parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+{
+ LSA_HANDLE DomainHandle = NULL;
+ LSA_HANDLE SecretHandle = NULL;
+ NTSTATUS Status;
+ PUNICODE_STRING OldPassword;
+ PUNICODE_STRING Password;
+ LSA_TRUST_INFORMATION DomainInformation;
+ PTRUSTED_DOMAIN_NAME_INFO NameInfo = NULL;
+ PTRUSTED_PASSWORD_INFO PasswordInfo;
+ UNICODE_STRING SecretName;
+
+ SecretName.Buffer = NULL;
+
+ //
+ // If the information is the domain name, try to create the domain.
+ //
+
+ if (InformationClass == TrustedDomainNameInformation) {
+ DomainInformation.Sid = TrustedDomainSid;
+ DomainInformation.Name = ((PTRUSTED_DOMAIN_NAME_INFO) Buffer)->Name;
+
+ Status = LsaCreateTrustedDomain(
+ PolicyHandle,
+ &DomainInformation,
+ 0, //desired access,
+ &DomainHandle
+ );
+ goto Cleanup;
+ }
+
+ //
+ // For posix offset, open the domain for SET_POSIX and call the old
+ // LSA API to set the offset.
+ //
+
+ if (InformationClass == TrustedPosixOffsetInformation) {
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ TRUSTED_SET_POSIX,
+ &DomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaSetInformationTrustedDomain(
+ DomainHandle,
+ InformationClass,
+ Buffer
+ );
+ goto Cleanup;
+ }
+
+ //
+ // The only only remaining allowed class is password information.
+ //
+
+ if (InformationClass != TrustedPasswordInformation) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ TRUSTED_QUERY_DOMAIN_NAME,
+ &DomainHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Get the name so we can find the secret name.
+ //
+
+ Status = LsaQueryInfoTrustedDomain(
+ DomainHandle,
+ TrustedDomainNameInformation,
+ &NameInfo
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Get the secret name
+ //
+
+ Status = LsapApiBuildSecretName(
+ NameInfo,
+ &SecretName
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &SecretName,
+ SECRET_SET_VALUE,
+ &SecretHandle
+ );
+
+ //
+ // If the secret didn't exist, create it now.
+ //
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ &SecretName,
+ SECRET_SET_VALUE,
+ &SecretHandle
+ );
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // If the old password wasn't specified, set it to be the new
+ // password.
+ //
+
+ PasswordInfo = (PTRUSTED_PASSWORD_INFO) Buffer;
+ Password = &PasswordInfo->Password;
+ if (PasswordInfo->OldPassword.Buffer == NULL) {
+ OldPassword = Password;
+ } else {
+ OldPassword = &PasswordInfo->OldPassword;
+ }
+
+ Status = LsaSetSecret(
+ SecretHandle,
+ Password,
+ OldPassword
+ );
+Cleanup:
+ if (SecretName.Buffer != NULL) {
+ MIDL_user_free(SecretName.Buffer);
+ }
+
+ if (DomainHandle != NULL) {
+ LsaClose(DomainHandle);
+ }
+
+ if (SecretHandle != NULL) {
+ LsaClose(SecretHandle);
+ }
+
+ if (NameInfo != NULL) {
+ LsaFreeMemory(NameInfo);
+ }
+
+ return(Status);
+
+
+}
+
+NTSTATUS
+NTAPI
+LsapDeleteTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes a trusted domain and the associated secret.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to delete
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient access to delete
+ the requested domain.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - The requested domain does not exist.
+
+--*/
+{
+ UNICODE_STRING SecretName;
+ NTSTATUS Status;
+ PTRUSTED_DOMAIN_NAME_INFO NameInfo = NULL;
+ LSA_HANDLE DomainHandle = NULL;
+ LSA_HANDLE SecretHandle = NULL;
+
+
+ SecretName.Buffer = NULL;
+
+ //
+ // Open the domain for query name and delete access. We need query name
+ // to find the secret name.
+ //
+
+ Status = LsaOpenTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid,
+ TRUSTED_QUERY_DOMAIN_NAME | DELETE,
+ &DomainHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Get the name so we can find the secret name.
+ //
+
+ Status = LsaQueryInfoTrustedDomain(
+ DomainHandle,
+ TrustedDomainNameInformation,
+ &NameInfo
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaDelete(DomainHandle);
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Zero the handle so we don't try to free it again.
+ //
+
+ DomainHandle = NULL;
+
+ //
+ // Get the secret name
+ //
+
+ Status = LsapApiBuildSecretName(
+ NameInfo,
+ &SecretName
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ &SecretName,
+ DELETE,
+ &SecretHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ //
+ // If the secret does not exist, that is o.k. - it means the password
+ // was never set.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ Status = STATUS_SUCCESS;
+ }
+ goto Cleanup;
+ }
+
+ Status = LsaDelete(SecretHandle);
+ if (NT_SUCCESS(Status)) {
+ //
+ // Zero the handle so we don't try to free it again.
+ //
+ SecretHandle = NULL;
+ }
+
+Cleanup:
+ if (NameInfo != NULL) {
+ LsaFreeMemory(NameInfo);
+ }
+ if (SecretName.Buffer != NULL) {
+ MIDL_user_free(SecretName.Buffer);
+ }
+ if (SecretHandle != NULL) {
+ LsaClose(SecretHandle);
+ }
+ if (DomainHandle != NULL) {
+ LsaClose(DomainHandle);
+ }
+
+ return(Status);
+
+
+
+}
+
+
+NTSTATUS
+NTAPI
+LsapStorePrivateData(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING KeyName,
+ IN OPTIONAL PUNICODE_STRING PrivateData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine stores private data in a secret named KeyName.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall. If this is the
+ first call, it requres POLICY_CREATE_SECRET access.
+
+ KeyName - Name of secret to store
+
+ PrivateData - Private data to store. If this is null, the secret is
+ deleted.
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient privilege to set
+ the workstation password.
+
+--*/
+
+{
+ LSA_HANDLE SecretHandle = NULL;
+ NTSTATUS Status;
+ ULONG DesiredAccess;
+ BOOLEAN DeleteSecret = FALSE;
+
+ //
+ // check whether to delete the secret or not.
+ //
+
+ if (ARGUMENT_PRESENT(PrivateData)) {
+ DesiredAccess = SECRET_SET_VALUE;
+ } else {
+ DesiredAccess = DELETE;
+ DeleteSecret = TRUE;
+ }
+
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ KeyName,
+ DesiredAccess,
+ &SecretHandle
+ );
+
+ if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) && !DeleteSecret) {
+ Status = LsaCreateSecret(
+ PolicyHandle,
+ KeyName,
+ DesiredAccess,
+ &SecretHandle
+ );
+
+
+ }
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ if (DeleteSecret) {
+ Status = LsaDelete(
+ SecretHandle
+ );
+
+ if (NT_SUCCESS(Status)) {
+ SecretHandle = NULL;
+ }
+ goto Cleanup;
+
+ }
+
+ Status = LsaSetSecret(
+ SecretHandle,
+ PrivateData,
+ PrivateData
+ );
+
+Cleanup:
+ if (SecretHandle != NULL) {
+ LsaClose(SecretHandle);
+ }
+
+ return(Status);
+
+
+}
+
+NTSTATUS
+NTAPI
+LsapRetrievePrivateData(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING KeyName,
+ OUT PUNICODE_STRING * PrivateData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the secret data stored under KeyName.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall
+
+ KeyName - Name of secret data to retrieve
+
+ PrivateData - Receives a pointer private data
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient access to get the
+ workstation password.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - there is no workstation password.
+
+--*/
+{
+ LSA_HANDLE SecretHandle = NULL;
+ NTSTATUS Status;
+
+ //
+ // Make the secret name
+ //
+
+
+ Status = LsaOpenSecret(
+ PolicyHandle,
+ KeyName,
+ SECRET_QUERY_VALUE,
+ &SecretHandle
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ Status = LsaQuerySecret(
+ SecretHandle,
+ PrivateData,
+ NULL,
+ NULL,
+ NULL
+ );
+Cleanup:
+ if (SecretHandle != NULL) {
+ LsaClose(SecretHandle);
+ }
+
+ return(Status);
+
+}
+
+/////////////////////////////////////////////////////////////////////////
+//
+// RPC wrappers for LSA APIs added in nt3.51. This routines call the
+// LSA, and if the interface doesn't exist, calls the LsapXXX routine
+// to accomplish the same task using the older routines.
+//
+////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+NTAPI
+LsaEnumerateAccountsWithUserRight(
+ IN LSA_HANDLE PolicyHandle,
+ IN OPTIONAL PUNICODE_STRING UserRight,
+ OUT PVOID *Buffer,
+ OUT PULONG CountReturned
+ )
+
+/*++
+
+Routine Description:
+
+
+ The LsaEnumerateAccounts API returns information about the accounts
+ in the target system's Lsa Database. This call requires
+ LSA_ENUMERATE_ACCOUNTS access to the Policy object. Since this call
+ accesses the privileges of an account, you must have PRIVILEGE_VIEW
+ access to the pseudo-privilege object.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ UserRight - Name of the right that the account must have.
+
+ Buffer - Receives a pointer to a LSA_ENUMERATION_INFORMATION structure
+ containing the SIDs of all the accounts.
+
+ CountReturned - Receives the number of sids returned.
+
+
+Return Values:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ STATUS_NO_MORE_ENTRIES - There are no more entries. This warning
+ is returned if no objects are enumerated because the
+ EnumerationContext value passed in is too high.
+--*/
+
+{
+ NTSTATUS Status;
+
+ LSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer;
+
+ EnumerationBuffer.EntriesRead = 0;
+ EnumerationBuffer.Information = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Enumerate the Accounts. On successful return,
+ // the Enumeration Buffer structure will receive a count
+ // of the number of Accounts enumerated this call
+ // and a pointer to an array of Account Information Entries.
+ //
+ // EnumerationBuffer -> EntriesRead
+ // Information -> Account Info for Domain 0
+ // Account Info for Domain 1
+ // ...
+ // Account Info for Domain
+ // (EntriesRead - 1)
+ //
+
+ Status = LsarEnumerateAccountsWithUserRight(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_UNICODE_STRING) UserRight,
+ &EnumerationBuffer
+ );
+
+ //
+ // Return enumeration information or NULL to caller.
+ //
+ // NOTE: "Information" is allocated by the called client stub
+ // as a single block via MIDL_user_allocate, because Information is
+ // allocated all-nodes. We can therefore pass back the pointer
+ // directly to the client, who will be able to free the memory after
+ // use via LsaFreeMemory() [which makes a MIDL_user_free call].
+ //
+
+ *CountReturned = EnumerationBuffer.EntriesRead;
+ *Buffer = EnumerationBuffer.Information;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the Account Information array,
+ // free it.
+ //
+
+ if (EnumerationBuffer.Information != NULL) {
+
+ MIDL_user_free(EnumerationBuffer.Information);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ //
+ // If the RPC server stub didn't exist, use the old version of the
+ // API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapEnumerateAccountsWithUserRight(
+ PolicyHandle,
+ UserRight,
+ Buffer,
+ CountReturned
+ );
+ }
+
+ return Status;
+
+}
+
+
+
+NTSTATUS
+NTAPI
+LsaEnumerateAccountRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ OUT PUNICODE_STRING *UserRights,
+ OUT PULONG CountOfRights
+ )
+
+
+/*++
+
+Routine Description:
+
+ Returns all the rights of an account. This is done by gathering the
+ privileges and system access of an account and translating that into
+ an array of strings.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall. This API requires
+ no special access.
+
+ AccountSid - Sid of account to open.
+
+ UserRights - receives an array of user rights (UNICODE_STRING) for
+ the account.
+
+ CountOfRights - receives the number of rights returned.
+
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - the caller did not have sufficient access to
+ return the privileges or system access of the account.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - the specified account did not exist.
+
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the
+ request.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAPR_USER_RIGHT_SET UserRightSet;
+
+ UserRightSet.Entries = 0;
+ UserRightSet.UserRights = NULL;
+
+ RpcTryExcept {
+
+
+ Status = LsarEnumerateAccountRights(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) AccountSid,
+ &UserRightSet
+ );
+
+ *CountOfRights = UserRightSet.Entries;
+ *UserRights = (PUNICODE_STRING) UserRightSet.UserRights;
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+ if (UserRightSet.UserRights != NULL) {
+ MIDL_user_free(UserRightSet.UserRights);
+ }
+
+ } RpcEndExcept;
+
+ //
+ // If the RPC server stub didn't exist, use the old version of the
+ // API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapEnumerateAccountRights(
+ PolicyHandle,
+ AccountSid,
+ UserRights,
+ CountOfRights
+ );
+
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LsaAddAccountRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ IN PUNICODE_STRING UserRights,
+ IN ULONG CountOfRights
+ )
+
+/*++
+
+Routine Description:
+
+ Adds rights to the account specified by the account sid. If the account
+ does not exist, it creates the account.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call. The handle must have
+ POLICY_CREATE_ACCOUNT access if this is the first call for this
+ AccountSid.
+
+ AccountSid - Sid of account to add rights to
+
+ UserRights - Array of unicode strings naming rights to add to the
+ account.
+
+Return Value:
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
+
+ STATUS_INVALID_PARAMTER - one of the parameters was not present
+
+ STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
+
+ STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
+ account to add privileges.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LSAPR_USER_RIGHT_SET UserRightSet;
+
+ UserRightSet.Entries = CountOfRights;
+ UserRightSet.UserRights = (PLSAPR_UNICODE_STRING) UserRights;
+
+ RpcTryExcept {
+
+ Status = LsarAddAccountRights(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) AccountSid,
+ &UserRightSet
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ //
+ // If the RPC server stub didn't exist, use the old version of the
+ // API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapAddAccountRights(
+ PolicyHandle,
+ AccountSid,
+ UserRights,
+ CountOfRights
+ );
+ }
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LsaRemoveAccountRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID AccountSid,
+ IN BOOLEAN AllRights,
+ IN PUNICODE_STRING UserRights,
+ IN ULONG CountOfRights
+ )
+
+/*++
+
+Routine Description:
+
+ Removes rights to the account specified by the account sid. If the
+ AllRights flag is set or if all the rights are removed, the account
+ is deleted.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call
+
+ AccountSid - Sid of account to remove rights from
+
+ UserRights - Array of unicode strings naming rights to remove from the
+ account.
+
+Return Value:
+ STATUS_INSUFFICIENT_RESOURCES - not enough memory to process the request
+
+ STATUS_INVALID_PARAMTER - one of the parameters was not present
+
+ STATUS_NO_SUCH_PRIVILEGE - One of the user rights was invalid
+
+ STATUS_ACCESS_DENIED - the caller does not have sufficient access to the
+ account to add privileges.
+
+--*/
+{
+ NTSTATUS Status;
+ LSAPR_USER_RIGHT_SET UserRightSet;
+
+ UserRightSet.Entries = CountOfRights;
+ UserRightSet.UserRights = (PLSAPR_UNICODE_STRING) UserRights;
+
+ RpcTryExcept {
+
+ Status = LsarRemoveAccountRights(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) AccountSid,
+ AllRights,
+ &UserRightSet
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapRemoveAccountRights(
+ PolicyHandle,
+ AccountSid,
+ AllRights,
+ UserRights,
+ CountOfRights
+ );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+LsaQueryTrustedDomainInfo(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ OUT PVOID *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The LsaQueryTrustedDomainInfo API obtains information from a
+ TrustedDomain object. The caller must have access appropriate to the
+ information being requested (see InformationClass parameter). It also
+ may query the secret object (for the TrustedDomainPasswordInformation
+ class).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to query.
+
+ InformationClass - Specifies the information to be returned.
+
+ Buffer - Receives a pointer to the buffer returned comtaining the
+ requested information. This buffer is allocated by this service
+ and must be freed when no longer needed by passing the returned
+ value to LsaFreeMemory().
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate
+ access to complete the operation.
+
+ STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
+ such as memory, to complete the call.
+--*/
+{
+ NTSTATUS Status;
+ PLSAP_CR_CIPHER_VALUE CipherPassword = NULL;
+ PLSAP_CR_CIPHER_VALUE CipherOldPassword = NULL;
+ PLSAP_CR_CLEAR_VALUE ClearPassword = NULL;
+ PLSAP_CR_CLEAR_VALUE ClearOldPassword = NULL;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+ ULONG DomainInfoSize;
+ PUCHAR Where = NULL;
+ PTRUSTED_PASSWORD_INFO PasswordInformation = NULL;
+
+ PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation = NULL;
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaQueryInformationTrustedDomain.
+ //
+
+ Status = LsarQueryTrustedDomainInfo(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) TrustedDomainSid,
+ InformationClass,
+ &TrustedDomainInformation
+ );
+
+ //
+ // Return pointer to Policy Information for the given class, or NULL.
+ //
+
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If memory was allocated for the returned Trusted Domain Information,
+ // free it.
+ //
+
+ if (TrustedDomainInformation != NULL) {
+
+ MIDL_user_free(TrustedDomainInformation);
+ }
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // if we aren't getting passwords, skip out here. Otherwise we need to
+ // decrypt the passwords.
+ //
+
+ if (InformationClass != TrustedPasswordInformation) {
+ *Buffer = TrustedDomainInformation;
+ TrustedDomainInformation = NULL;
+ goto Cleanup;
+ }
+
+ //
+ // Obtain the Session Key to be used to two-way encrypt the
+ // Current Value and/or Old Values.
+ //
+
+ RpcTryExcept {
+
+ Status = LsapCrClientGetSessionKey( PolicyHandle, &SessionKey );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // If the Current Value is requested and a Current Value exists,
+ // decrypt it using the Session key. Otherwise store NULL for return.
+ //
+
+ if (TrustedDomainInformation->TrustedPasswordInfo.Password != NULL) {
+
+ Status = LsapCrDecryptValue(
+ (PLSAP_CR_CIPHER_VALUE)
+ TrustedDomainInformation->TrustedPasswordInfo.Password,
+ SessionKey,
+ &ClearPassword
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // Convert Clear Current Value to Unicode
+ //
+
+ LsapCrClearValueToUnicode(
+ ClearPassword,
+ (PUNICODE_STRING) ClearPassword
+ );
+
+
+ }
+
+ //
+ // Get the old password
+ //
+
+ if (TrustedDomainInformation->TrustedPasswordInfo.OldPassword != NULL) {
+
+ Status = LsapCrDecryptValue(
+ (PLSAP_CR_CIPHER_VALUE)
+ TrustedDomainInformation->TrustedPasswordInfo.OldPassword,
+ SessionKey,
+ &ClearOldPassword
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // Convert Clear Current Value to Unicode
+ //
+
+ LsapCrClearValueToUnicode(
+ ClearOldPassword,
+ (PUNICODE_STRING) ClearOldPassword
+ );
+
+
+ }
+
+
+ MIDL_user_free(TrustedDomainInformation);
+ TrustedDomainInformation = NULL;
+
+
+ //
+ // Allocate a buffer for the two passwords and marshall the
+ // passwords into the buffer.
+ //
+
+ DomainInfoSize = sizeof(TRUSTED_PASSWORD_INFO);
+
+ if (ClearPassword != NULL) {
+
+ DomainInfoSize += ((PUNICODE_STRING) ClearPassword)->MaximumLength;
+ }
+ if (ClearOldPassword != NULL) {
+
+ DomainInfoSize += ((PUNICODE_STRING) ClearOldPassword)->MaximumLength;
+ }
+
+ PasswordInformation = (PTRUSTED_PASSWORD_INFO) MIDL_user_allocate(DomainInfoSize);
+ if (PasswordInformation == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ Where = (PUCHAR) (PasswordInformation+1);
+
+ if (ClearPassword != NULL)
+ {
+ PasswordInformation->Password = *(PUNICODE_STRING) ClearPassword;
+ PasswordInformation->Password.Buffer = (LPWSTR) Where;
+ Where += PasswordInformation->Password.MaximumLength;
+ RtlCopyUnicodeString(
+ &PasswordInformation->Password,
+ (PUNICODE_STRING) ClearPassword
+ );
+ }
+
+ if (ClearOldPassword != NULL)
+ {
+ PasswordInformation->OldPassword = *(PUNICODE_STRING) ClearOldPassword;
+ PasswordInformation->OldPassword.Buffer = (LPWSTR) Where;
+ Where += PasswordInformation->OldPassword.MaximumLength;
+ RtlCopyUnicodeString(
+ &PasswordInformation->OldPassword,
+ (PUNICODE_STRING) ClearOldPassword
+ );
+ }
+ ASSERT(Where - (PUCHAR) PasswordInformation == (LONG) DomainInfoSize);
+
+ *Buffer = PasswordInformation;
+ PasswordInformation = NULL;
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ //
+ // If necessary, free memory allocated for the Session Key.
+ //
+
+ if (SessionKey != NULL) {
+
+ MIDL_user_free(SessionKey);
+ }
+
+ //
+ // If necessary, free memory allocated for the returned Encrypted
+ // Current Value.
+ //
+
+ if (CipherPassword != NULL) {
+
+ LsapCrFreeMemoryValue(CipherPassword);
+ }
+
+ //
+ // If necessary, free memory allocated for the returned Encrypted
+ // Old Value.
+ //
+
+ if (CipherOldPassword != NULL) {
+
+ LsapCrFreeMemoryValue(CipherOldPassword);
+ }
+ if (ClearPassword != NULL) {
+
+ LsapCrFreeMemoryValue(ClearPassword);
+ }
+ if (ClearOldPassword != NULL) {
+
+ LsapCrFreeMemoryValue(ClearOldPassword);
+ }
+
+ if (TrustedDomainInformation != NULL) {
+ MIDL_user_free(TrustedDomainInformation);
+ }
+
+ if (PasswordInformation != NULL) {
+ MIDL_user_free(PasswordInformation);
+ }
+
+ //
+ // If the error was that the server stub didn't exist, call
+ // the old version of the API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapQueryTrustedDomainInfo(
+ PolicyHandle,
+ TrustedDomainSid,
+ InformationClass,
+ Buffer
+ );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+LsaSetTrustedDomainInformation(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid,
+ IN TRUSTED_INFORMATION_CLASS InformationClass,
+ IN PVOID Buffer
+ )
+
+
+/*++
+
+Routine Description:
+
+
+ The LsaSetTrustedDomainInformation API modifies information in the Trusted
+ Domain Object and in the Secret Object. The caller must have access
+ appropriate to the information to be changed in the Policy Object, see
+ the InformationClass parameter.
+
+ If the domain does not yet exist and the information class is
+ TrustedDomainNameInformation, then the domain is created. If the
+ domain exists and the class is TrustedDomainNameInformation, an
+ error is returned.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to modify.
+
+ InformationClass - Specifies the type of information being changed.
+ The information types and accesses required to change them are as
+ follows:
+
+ TrustedDomainNameInformation POLICY_TRUST_ADMIN
+ TrustedPosixOffsetInformation none
+ TrustedPasswordInformation POLICY_CREATE_SECRET
+
+ Buffer - Points to a structure containing the information appropriate
+ to the InformationClass parameter.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_ACCESS_DENIED - Caller does not have the appropriate access
+ to complete the operation.
+
+ Others TBS
+--*/
+
+{
+ NTSTATUS Status;
+ PLSAPR_TRUSTED_DOMAIN_INFO DomainInformation;
+ LSAPR_TRUSTED_PASSWORD_INFO LsaPasswordInfo;
+ PTRUSTED_PASSWORD_INFO PasswordInformation;
+ PLSAP_CR_CIPHER_VALUE CipherPassword = NULL;
+ LSAP_CR_CLEAR_VALUE ClearPassword;
+ PLSAP_CR_CIPHER_VALUE CipherOldPassword = NULL;
+ LSAP_CR_CLEAR_VALUE ClearOldPassword;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+ PUNICODE_STRING OldPassword;
+
+ //
+ // If the infotype is TrustedPasswordInformation, then we need to
+ // setup a secure channel to transmit the secret passwords.
+ //
+
+ if (InformationClass == TrustedPasswordInformation) {
+
+ PasswordInformation = (PTRUSTED_PASSWORD_INFO) Buffer;
+ LsaPasswordInfo.Password = NULL;
+ LsaPasswordInfo.OldPassword = NULL;
+
+ //
+ // Obtain the Session Key to be used to two-way encrypt the
+ // Current Value.
+ //
+
+ RpcTryExcept {
+
+ Status = LsapCrClientGetSessionKey( PolicyHandle, &SessionKey );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // The password must be specified, even if it is an empty string.
+ //
+
+ if (PasswordInformation->Password.Buffer != NULL) {
+
+ //
+ // Convert input from Unicode Structures to Clear Value Structures.
+ //
+
+ LsapCrUnicodeToClearValue(
+ &PasswordInformation->Password,
+ &ClearPassword
+ );
+
+
+
+ //
+ // Encrypt the Current Value if specified and not too long.
+ //
+
+
+ Status = LsapCrEncryptValue(
+ &ClearPassword,
+ SessionKey,
+ &CipherPassword
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+ LsaPasswordInfo.Password = (PLSAPR_CR_CIPHER_VALUE) CipherPassword;
+
+ //
+ // If the old password wasn't specified, set it to be the
+ // new password.
+ //
+
+ if (PasswordInformation->OldPassword.Buffer == NULL) {
+ OldPassword = &PasswordInformation->Password;
+ } else {
+ OldPassword = &PasswordInformation->OldPassword;
+ }
+
+
+ //
+ // Convert input from Unicode Structures to Clear Value Structures.
+ //
+
+ LsapCrUnicodeToClearValue(
+ OldPassword,
+ &ClearOldPassword
+ );
+
+
+
+ //
+ // Encrypt the Current Value if specified and not too long.
+ //
+
+
+ Status = LsapCrEncryptValue(
+ &ClearOldPassword,
+ SessionKey,
+ &CipherOldPassword
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+ LsaPasswordInfo.OldPassword = (PLSAPR_CR_CIPHER_VALUE) CipherOldPassword;
+ } else {
+ Status = STATUS_INVALID_PARAMETER;
+ goto Cleanup;
+ }
+
+ DomainInformation = (PLSAPR_TRUSTED_DOMAIN_INFO) &LsaPasswordInfo;
+
+ } else {
+ DomainInformation = (PLSAPR_TRUSTED_DOMAIN_INFO) Buffer;
+ }
+
+ RpcTryExcept {
+
+ //
+ // Call the Client Stub for LsaSetInformationTrustedDomain
+ //
+
+ Status = LsarSetTrustedDomainInfo
+ (
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) TrustedDomainSid,
+ InformationClass,
+ DomainInformation
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+Cleanup:
+ if (SessionKey != NULL) {
+ MIDL_user_free(SessionKey);
+ }
+ if (CipherPassword != NULL) {
+ LsaFreeMemory(CipherPassword);
+ }
+ if (CipherOldPassword != NULL) {
+ LsaFreeMemory(CipherOldPassword);
+ }
+
+ //
+ // If the error was that the server stub didn't exist, call
+ // the old version of the API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapSetTrustedDomainInformation(
+ PolicyHandle,
+ TrustedDomainSid,
+ InformationClass,
+ Buffer
+ );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LsaDeleteTrustedDomain(
+ IN LSA_HANDLE PolicyHandle,
+ IN PSID TrustedDomainSid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes a trusted domain and the associated secret.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call.
+
+ TrustedDomainSid - Sid of domain to delete
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient access to delete
+ the requested domain.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - The requested domain does not exist.
+
+--*/
+{
+ NTSTATUS Status;
+
+ RpcTryExcept {
+
+
+ Status = LsarDeleteTrustedDomain(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_SID) TrustedDomainSid
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ //
+ // If the error was that the server stub didn't exist, call
+ // the old version of the API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapDeleteTrustedDomain(
+ PolicyHandle,
+ TrustedDomainSid
+ );
+ }
+
+ return(Status);
+}
+
+//
+// This API sets the workstation password (equivalent of setting/getting
+// the SSI_SECRET_NAME secret)
+//
+
+NTSTATUS
+NTAPI
+LsaStorePrivateData(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING KeyName,
+ IN OPTIONAL PUNICODE_STRING PrivateData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine stores private data in an LSA secret named KeyName.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall. If this is the
+ first call, it requres POLICY_CREATE_SECRET access.
+
+ KeyName - Name of secret to store.
+
+ PrivateData - Data to store. If not present, the secret is deleted.
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient privilege to set
+ the workstation password.
+
+--*/
+
+
+{
+ NTSTATUS Status;
+
+ PLSAP_CR_CIPHER_VALUE CipherCurrentValue = NULL;
+ LSAP_CR_CLEAR_VALUE ClearCurrentValue;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+
+ if (ARGUMENT_PRESENT(PrivateData)) {
+
+ //
+ // Convert input from Unicode Structures to Clear Value Structures.
+ //
+
+
+ LsapCrUnicodeToClearValue( PrivateData, &ClearCurrentValue );
+
+ //
+ // Obtain the Session Key to be used to two-way encrypt the
+ // Current Value.
+ //
+
+ RpcTryExcept {
+
+ Status = LsapCrClientGetSessionKey( PolicyHandle, &SessionKey );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+ //
+ // Encrypt the Current Value if specified and not too long.
+ //
+
+
+ Status = LsapCrEncryptValue(
+ &ClearCurrentValue,
+ SessionKey,
+ &CipherCurrentValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+
+ }
+
+ //
+ // Set the Secret Values.
+ //
+
+ RpcTryExcept {
+
+ Status = LsarStorePrivateData(
+ (LSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_UNICODE_STRING) KeyName,
+ (PLSAPR_CR_CIPHER_VALUE) CipherCurrentValue
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ //
+ // If necessary, free memory allocated for the Encrypted Current Value.
+ //
+
+ if (CipherCurrentValue != NULL) {
+
+ LsaFreeMemory(CipherCurrentValue);
+ }
+
+ //
+ // If necessary, free memory allocated for the Session Key.
+ //
+
+ if (SessionKey != NULL) {
+
+ MIDL_user_free(SessionKey);
+ }
+
+ //
+ // If the error was that the server stub didn't exist, call
+ // the old version of the API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapStorePrivateData(
+ PolicyHandle,
+ KeyName,
+ PrivateData
+ );
+ }
+
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+NTAPI
+LsaRetrievePrivateData(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING KeyName,
+ OUT PUNICODE_STRING *PrivateData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the secret stored in KeyName.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall
+
+ KeyName - Name of secret to retrieve
+
+ PrivateData - Receives private data, should be freed with LsaFreeMemory.
+
+
+Return Value:
+
+ STATUS_ACCESS_DENIED - caller has insufficient access to get the
+ private data.
+
+ STATUS_OBJECT_NAME_NOT_FOUND - there is no private data stored under
+ KeyName.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PLSAP_CR_CIPHER_VALUE CipherCurrentValue = NULL;
+ PLSAP_CR_CLEAR_VALUE ClearCurrentValue = NULL;
+ PLSAP_CR_CIPHER_KEY SessionKey = NULL;
+
+ RpcTryExcept {
+
+ Status = LsarRetrievePrivateData(
+ (PLSAPR_HANDLE) PolicyHandle,
+ (PLSAPR_UNICODE_STRING) KeyName,
+ (PLSAPR_CR_CIPHER_VALUE *) &CipherCurrentValue
+ );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ //
+ // Obtain the Session Key to be used to two-way encrypt the
+ // Current Value and/or Old Values.
+ //
+
+ RpcTryExcept {
+
+ Status = LsapCrClientGetSessionKey( PolicyHandle, &SessionKey );
+
+ } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = LsapApiReturnResult(I_RpcMapWin32Status(RpcExceptionCode()));
+
+ } RpcEndExcept;
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ //
+ // If the Current Value is requested and a Current Value exists,
+ // decrypt it using the Session key. Otherwise store NULL for return.
+ //
+
+ if (CipherCurrentValue != NULL) {
+
+ Status = LsapCrDecryptValue(
+ CipherCurrentValue,
+ SessionKey,
+ &ClearCurrentValue
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto QuerySecretError;
+ }
+
+ //
+ // Convert Clear Current Value to Unicode
+ //
+
+ LsapCrClearValueToUnicode(
+ ClearCurrentValue,
+ (PUNICODE_STRING) ClearCurrentValue
+ );
+ *PrivateData = (PUNICODE_STRING) ClearCurrentValue;
+
+ } else {
+
+ *PrivateData = NULL;
+ }
+
+
+
+
+QuerySecretFinish:
+
+ //
+ // If necessary, free memory allocated for the Session Key.
+ //
+
+ if (SessionKey != NULL) {
+
+ MIDL_user_free(SessionKey);
+ }
+
+ //
+ // If necessary, free memory allocated for the returned Encrypted
+ // Current Value.
+ //
+
+ if (CipherCurrentValue != NULL) {
+
+ LsapCrFreeMemoryValue(CipherCurrentValue);
+ }
+
+
+ //
+ // If the error was that the server stub didn't exist, call
+ // the old version of the API.
+ //
+
+ if ((Status == RPC_NT_UNKNOWN_IF) ||
+ (Status == RPC_NT_PROCNUM_OUT_OF_RANGE)) {
+
+ Status = LsapRetrievePrivateData(
+ PolicyHandle,
+ KeyName,
+ PrivateData
+ );
+ }
+
+
+
+ return(Status);
+
+QuerySecretError:
+
+ //
+ // If necessary, free memory allocated for the Clear Current Value
+ //
+
+ if (ClearCurrentValue != NULL) {
+
+ LsapCrFreeMemoryValue(ClearCurrentValue);
+ }
+
+
+ *PrivateData = NULL;
+
+
+ goto QuerySecretFinish;
+}
+
+
+
+NTSTATUS
+LsapApiConvertRightsToPrivileges(
+ IN LSA_HANDLE PolicyHandle,
+ IN PUNICODE_STRING UserRights,
+ IN ULONG RightCount,
+ OUT PPRIVILEGE_SET * Privileges,
+ OUT PULONG SystemAccess
+ )
+/*++
+
+Routine Description:
+
+ Converts an array of user rights (unicode strings) into a privilege set
+ and a system access flag.
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicyCall, requires POLICY_LOOKUP_NAME
+ access.
+
+ UserRights - Array of user rights
+
+ RightCount - Count of user rights
+
+ Privileges - Receives privilege set, should be freed with MIDL_user_free
+
+ SystemAccess - Receives system access flags.
+
+Return Value:
+
+--*/
+
+{
+ ULONG RightIndex;
+ ULONG PrivilegeIndex;
+ ULONG AccessIndex;
+ PPRIVILEGE_SET PrivilegeSet = NULL;
+ ULONG Access = 0;
+ ULONG PrivilegeSetSize = 0;
+ NTSTATUS Status;
+ LUID PrivilegeValue;
+
+ //
+ // if we weren't passed any privileges, don't allocate anything.
+ //
+
+ if (RightCount == 0) {
+
+ *Privileges = NULL;
+ SystemAccess = 0;
+ return(STATUS_SUCCESS);
+ }
+
+ //
+ // Compute the size of the privilege set. We actually over estimate
+ // by assuming that all the rights are privileges. We subtract one
+ // from RightCount to take into account the fact that a PRIVILEGE_SET
+ // has one LUID_AND_ATTRIBUTE in it.
+ //
+
+
+ PrivilegeSetSize = sizeof(PRIVILEGE_SET) +
+ (RightCount-1) * sizeof(LUID_AND_ATTRIBUTES);
+
+ PrivilegeSet = (PPRIVILEGE_SET) MIDL_user_allocate(PrivilegeSetSize);
+
+ if (PrivilegeSet == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ //
+ // Try looking up every right. If we find it as a privilege,
+ // add it to the privilege set.
+ //
+
+ PrivilegeIndex = 0;
+
+ for (RightIndex = 0; RightIndex < RightCount ; RightIndex++) {
+ Status = LsaLookupPrivilegeValue(
+ PolicyHandle,
+ &UserRights[RightIndex],
+ &PrivilegeValue
+ );
+ if (NT_SUCCESS(Status)) {
+ PrivilegeSet->Privilege[PrivilegeIndex].Luid = PrivilegeValue;
+ PrivilegeSet->Privilege[PrivilegeIndex].Attributes = 0;
+ PrivilegeIndex++;
+
+ } else if (Status != STATUS_NO_SUCH_PRIVILEGE) {
+ //
+ // This is a more serious error - bail here.
+ //
+
+ goto Cleanup;
+ } else {
+
+ //
+ // Try looking up the right as a system access type.
+ //
+
+ for (AccessIndex = 0; AccessIndex < LSAP_DB_SYSTEM_ACCESS_TYPES ; AccessIndex++) {
+ if (RtlCompareUnicodeString(
+ &UserRights[RightIndex],
+ &LsapDbRightAndAccess[AccessIndex].UserRight,
+ FALSE // case sensitive
+ ) == 0) {
+ Access |= LsapDbRightAndAccess[AccessIndex].SystemAccess;
+ break;
+ }
+ }
+
+ //
+ // If we went through the access types without finding the right,
+ // it must not be valid so escape here.
+ //
+
+ if (AccessIndex == LSAP_DB_SYSTEM_ACCESS_TYPES) {
+ Status = STATUS_NO_SUCH_PRIVILEGE;
+ goto Cleanup;
+ }
+
+ }
+ }
+
+ PrivilegeSet->Control = 0;
+ PrivilegeSet->PrivilegeCount = PrivilegeIndex;
+
+ *Privileges = PrivilegeSet;
+ *SystemAccess = Access;
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+ if (!NT_SUCCESS(Status)) {
+ if (PrivilegeSet != NULL) {
+ LsaFreeMemory(PrivilegeSet);
+ }
+ }
+
+ return(Status);
+
+}
+
+NTSTATUS
+LsapApiConvertPrivilegesToRights(
+ IN LSA_HANDLE PolicyHandle,
+ IN OPTIONAL PPRIVILEGE_SET Privileges,
+ IN OPTIONAL ULONG SystemAccess,
+ OUT PUNICODE_STRING * UserRights,
+ OUT PULONG RightCount
+ )
+/*++
+
+Routine Description:
+
+ Converts a privilege set and a system access flag into an array of
+ user rights (unicode strings).
+
+Arguments:
+
+ PolicyHandle - Handle from an LsaOpenPolicy call, must have
+ POLICY_LOOKUP_NAMES access.
+
+ Privileges - Privilege set to convert
+
+ SystemAccess - System access flags to convert
+
+ UserRights - Receives an array of user rights (unicode strings). Should
+ be freed with MIDL_user_free
+
+ RightCount - Receives count of rights in UserRights array
+
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status;
+ PUNICODE_STRING OutputRights = NULL;
+ PUNICODE_STRING * PrivilegeNames = NULL;
+ UNICODE_STRING AccessNames[LSAP_DB_SYSTEM_ACCESS_TYPES];
+ ULONG RightSize;
+ ULONG PrivilegeSize;
+ ULONG Count;
+ ULONG PrivilegeIndex;
+ ULONG AccessIndex;
+ ULONG RightIndex;
+ ULONG AccessCount;
+ PUCHAR Where;
+
+ //
+ // Compute the size of the temporary array. This is just an array of
+ // pointers to unicode strings to hold the privilege names until
+ // we reallocate them into one big buffer.
+ //
+
+ RightSize = 0;
+ Count = 0;
+ if (ARGUMENT_PRESENT(Privileges)) {
+
+ PrivilegeSize = Privileges->PrivilegeCount * sizeof(PUNICODE_STRING);
+ PrivilegeNames = (PUNICODE_STRING *) MIDL_user_allocate(PrivilegeSize);
+
+ if (PrivilegeNames == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ RtlZeroMemory(
+ PrivilegeNames,
+ PrivilegeSize
+ );
+
+ //
+ // Lookup the privilge name and store it in the temporary array
+ //
+
+ for (PrivilegeIndex = 0; PrivilegeIndex < Privileges->PrivilegeCount ;PrivilegeIndex++ ) {
+
+ Status = LsaLookupPrivilegeName(
+ PolicyHandle,
+ &Privileges->Privilege[PrivilegeIndex].Luid,
+ &PrivilegeNames[PrivilegeIndex]
+ );
+ if (!NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+ RightSize += sizeof(UNICODE_STRING) + PrivilegeNames[PrivilegeIndex]->MaximumLength;
+ }
+ }
+
+ //
+ // Now convert the system access flags to user rights.
+ //
+
+ if (ARGUMENT_PRESENT(SystemAccess)) {
+
+ AccessCount = 0;
+ for (AccessIndex = 0; AccessIndex < LSAP_DB_SYSTEM_ACCESS_TYPES ; AccessIndex++) {
+
+ if ((SystemAccess & LsapDbRightAndAccess[AccessIndex].SystemAccess) != 0) {
+
+ AccessNames[AccessCount] = LsapDbRightAndAccess[AccessIndex].UserRight;
+ RightSize += sizeof(UNICODE_STRING) + AccessNames[AccessCount].MaximumLength;
+ AccessCount++;
+ }
+ }
+ }
+
+ //
+ // Allocate the output buffer and start copying the strings into the
+ // buffer.
+ //
+
+ Count = Privileges->PrivilegeCount + AccessCount;
+
+ OutputRights = (PUNICODE_STRING) MIDL_user_allocate(RightSize);
+ if (OutputRights == NULL) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ Where = (PUCHAR) OutputRights + (Count * sizeof(UNICODE_STRING));
+
+ //
+ // Copy in the privileges first
+ //
+
+ RightIndex = 0;
+ for (PrivilegeIndex = 0; PrivilegeIndex < Privileges->PrivilegeCount ; PrivilegeIndex ++) {
+
+ OutputRights[RightIndex] = *PrivilegeNames[PrivilegeIndex];
+ OutputRights[RightIndex].Buffer = (LPWSTR) Where;
+ RtlCopyMemory(
+ Where,
+ PrivilegeNames[PrivilegeIndex]->Buffer,
+ OutputRights[RightIndex].MaximumLength
+ );
+ Where += OutputRights[RightIndex].MaximumLength;
+ RightIndex++;
+ }
+
+ //
+ // Now copy in the access types
+ //
+
+ for (AccessIndex = 0; AccessIndex < AccessCount; AccessIndex++) {
+
+ OutputRights[RightIndex] = AccessNames[AccessIndex];
+ OutputRights[RightIndex].Buffer = (LPWSTR) Where;
+ RtlCopyMemory(
+ Where,
+ AccessNames[AccessIndex].Buffer,
+ OutputRights[RightIndex].MaximumLength
+ );
+ Where += OutputRights[RightIndex].MaximumLength;
+ RightIndex++;
+ }
+
+ ASSERT(RightIndex == Count);
+
+ *UserRights = OutputRights;
+ OutputRights = NULL;
+ *RightCount = Count;
+
+ Status = STATUS_SUCCESS;
+
+Cleanup:
+
+ if (PrivilegeNames != NULL) {
+ for (PrivilegeIndex = 0; PrivilegeIndex < Privileges->PrivilegeCount ; PrivilegeIndex++) {
+ if (PrivilegeNames[PrivilegeIndex] != NULL) {
+ LsaFreeMemory(PrivilegeNames[PrivilegeIndex]);
+ }
+ }
+ MIDL_user_free(PrivilegeNames);
+ }
+
+ if (OutputRights != NULL) {
+ MIDL_user_free(OutputRights);
+ }
+
+ return(Status);
+}
+
+
+
+DWORD
+LsaNtStatusToWinError(
+ IN NTSTATUS Status
+ )
+/*++
+
+Routine Description:
+
+ This routine converts and NTSTATUS to an win32 error code. It is used
+ by people who want to call the LSA APIs but are writing for a win32
+ environment.
+
+Arguments:
+
+ Status - The status code to be mapped
+
+Return Value:
+
+ The return from RtlNtStatusToDosError. If the error could not be
+ mapped, then ERROR_MR_MID_NOT_FOUND is returned.
+
+--*/
+
+{
+ return(RtlNtStatusToDosError(Status));
+}
diff --git a/private/lsa/uclient/rpcbind.c b/private/lsa/uclient/rpcbind.c
new file mode 100644
index 000000000..a75ab1399
--- /dev/null
+++ b/private/lsa/uclient/rpcbind.c
@@ -0,0 +1,102 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ rpcbind.c
+
+Abstract:
+
+ LSA - Client RPC Binding Routines
+
+Author:
+
+ Scott Birrell (ScottBi) April 30, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsaclip.h"
+
+#include <ntrpcp.h> // prototypes for MIDL user functions
+
+handle_t
+PLSAPR_SERVER_NAME_bind (
+ IN OPTIONAL PLSAPR_SERVER_NAME ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the LSA client stubs when
+ it is necessary to bind to the LSA on some server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+
+{
+ handle_t BindingHandle;
+ NTSTATUS Status;
+
+ Status = RpcpBindRpc (
+ ServerName,
+ L"lsarpc",
+ 0,
+ &BindingHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("PLSAPR_SERVER_NAME_bind: RpcpBindRpc failed 0x%lx\n", Status);
+
+ }
+
+ return( BindingHandle);
+}
+
+
+VOID
+PLSAPR_SERVER_NAME_unbind (
+ IN OPTIONAL PLSAPR_SERVER_NAME ServerName,
+ IN handle_t BindingHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the LSA client stubs when
+ it is necessary to unbind from the LSA server.
+
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ RpcpUnbindRpc ( BindingHandle );
+ return;
+
+ UNREFERENCED_PARAMETER( ServerName ); // This parameter is not used
+}
diff --git a/private/lsa/uclient/rpcclimm.c b/private/lsa/uclient/rpcclimm.c
new file mode 100644
index 000000000..f23f95a9e
--- /dev/null
+++ b/private/lsa/uclient/rpcclimm.c
@@ -0,0 +1,103 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ rpcclimm.c
+
+Abstract:
+
+ LSA - Client RPC Memory Management Routines
+
+ NOTE: These routines use windows API, so are different from
+ their server counterparts.
+Author:
+
+ Scott Birrell (ScottBi) May 1, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "lsaclip.h"
+/*
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+#include <lsarpc.h> // generated by the MIDL complier
+#include <nturtl.h> // needed for windows.h
+#include <windows.h> // LocalAlloc
+#include <string.h> // for strcpy strcat strlen memcmp
+*/
+
+
+PVOID
+MIDL_user_allocate (
+ unsigned int NumBytes
+ )
+
+/*++
+
+Routine Description:
+
+ Allocates storage for RPC server transactions. The RPC stubs will
+ either call MIDL_user_allocate when it needs to un-marshall data into a
+ buffer that the user must free. RPC servers will use MIDL_user_allocate to
+ allocate storage that the RPC server stub will free after marshalling
+ the data.
+
+Arguments:
+
+ NumBytes - The number of bytes to allocate.
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+
+{
+ return (LocalAlloc(LMEM_FIXED,NumBytes));
+}
+
+
+
+VOID
+MIDL_user_free (
+ void *MemPointer
+ )
+
+/*++
+
+Routine Description:
+
+ Frees storage used in RPC transactions. The RPC client can call this
+ function to free buffer space that was allocated by the RPC client
+ stub when un-marshalling data that is to be returned to the client.
+ The Client calls MIDL_user_free when it is finished with the data and
+ desires to free up the storage.
+ The RPC server stub calls MIDL_user_free when it has completed
+ marshalling server data that is to be passed back to the client.
+
+Arguments:
+
+ MemPointer - This points to the memory block that is to be released.
+
+Return Value:
+
+ none.
+
+Note:
+
+
+--*/
+
+{
+ LocalFree(MemPointer);
+}
diff --git a/private/lsa/uclient/runsamdb.cmd b/private/lsa/uclient/runsamdb.cmd
new file mode 100644
index 000000000..0eb1e5b42
--- /dev/null
+++ b/private/lsa/uclient/runsamdb.cmd
@@ -0,0 +1,4 @@
+echo To start run, press enter in reply to Enter the new time prompt
+time
+ctsamdb %1
+time
diff --git a/private/lsa/uclient/sources b/private/lsa/uclient/sources
new file mode 100644
index 000000000..d28efe8d0
--- /dev/null
+++ b/private/lsa/uclient/sources
@@ -0,0 +1,44 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lsa
+MINORCOMP=uclient
+
+TARGETNAME=lsaudll
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\inc;..\..\inc
+
+SOURCES= crclient.c \
+ rpcapi.c \
+ rpcapi2.c \
+ rpcbind.c \
+ lsarpc_c.c
+
+UMTYPE=windows
+
+UMTEST=ctlkacct*ctsamdb*ctlklsa
+
+C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -D_ADVAPI32_
diff --git a/private/lsa/uclient/tgetsid.c b/private/lsa/uclient/tgetsid.c
new file mode 100644
index 000000000..eefe21b2b
--- /dev/null
+++ b/private/lsa/uclient/tgetsid.c
@@ -0,0 +1,211 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ tgetsid.c
+
+Abstract:
+
+ This file is a temporary test for querying the machine's SID.
+
+Author:
+
+ Jim Kelly (JimK) 8-10-1994
+
+Environment:
+
+ User Mode - Win32
+
+ to build: nmake UMTYPE=console UMTEST=tgetsid
+
+Revision History:
+
+
+--*/
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Includes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <ntlsa.h>
+#include <stdio.h>
+#include <string.h>
+
+BOOLEAN
+GetMachineSid(
+ OUT PSID *Sid,
+ OUT PULONG SidLength
+ );
+
+VOID _CRTAPI1
+main( VOID )
+
+{
+
+ BOOLEAN
+ Result;
+
+ PSID
+ Sid;
+
+ PISID
+ ISid;
+
+ ULONG
+ i,
+ SubCount,
+ Tmp,
+ SidLength;
+
+
+ Result = GetMachineSid( &Sid, &SidLength );
+
+ if (Result) {
+
+ ISid = (PISID)Sid; // pointer to opaque structure
+
+ printf("S-%u-", (USHORT)ISid->Revision );
+
+ if ( (ISid->IdentifierAuthority.Value[0] != 0) ||
+ (ISid->IdentifierAuthority.Value[1] != 0) ){
+ printf("0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)ISid->IdentifierAuthority.Value[0],
+ (USHORT)ISid->IdentifierAuthority.Value[1],
+ (USHORT)ISid->IdentifierAuthority.Value[2],
+ (USHORT)ISid->IdentifierAuthority.Value[3],
+ (USHORT)ISid->IdentifierAuthority.Value[4],
+ (USHORT)ISid->IdentifierAuthority.Value[5] );
+ } else {
+ Tmp = (ULONG)ISid->IdentifierAuthority.Value[5] +
+ (ULONG)(ISid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(ISid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(ISid->IdentifierAuthority.Value[2] << 24);
+ printf("%lu", Tmp);
+ }
+
+ SubCount = (ULONG)(*RtlSubAuthorityCountSid( Sid ));
+ for (i = 0; i<SubCount; i++) {
+ printf("-0x%lx", (*RtlSubAuthoritySid( Sid, i)));
+ }
+
+ }
+
+ printf("\n\n Th Tha That's all folks\n");
+
+
+
+}
+
+
+BOOLEAN
+GetMachineSid(
+ OUT PSID *Sid,
+ OUT PULONG SidLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine retrieves the sid of this machine's account
+ domain and returns it in memory allocated with RtlAllocateHeap().
+ If this machine is a server in a domain, then this SID will
+ be domain's SID.
+
+
+Arguments:
+
+ Sid - receives a pointer to the returned SID.
+
+ SidLength - Receives the length (in bytes) of the returned SID.
+
+
+Return Value:
+
+ TRUE - The SID was allocated and returned.
+
+ FALSE - Some error prevented the SID from being returned.
+
+--*/
+
+{
+ NTSTATUS
+ Status;
+
+ OBJECT_ATTRIBUTES
+ ObjectAttributes;
+
+ LSA_HANDLE
+ PolicyHandle;
+
+ POLICY_ACCOUNT_DOMAIN_INFO
+ *DomainInfo = NULL;
+
+ PSID
+ ReturnSid;
+
+ BOOLEAN
+ ReturnStatus = FALSE;
+
+
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ NULL, // No name
+ 0, // No attributes
+ 0, // No root handle
+ NULL // No SecurityDescriptor
+ );
+
+ Status = LsaOpenPolicy( NULL, // Local System
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = LsaQueryInformationPolicy(
+ PolicyHandle,
+ PolicyAccountDomainInformation,
+ &DomainInfo
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ ASSERT(DomainInfo != NULL);
+
+ //
+ // Allocate the return buffer
+ //
+
+ (*SidLength) = RtlLengthSid( DomainInfo->DomainSid );
+ ReturnSid = RtlAllocateHeap( RtlProcessHeap(), 0, (*SidLength) );
+
+ if (ReturnSid != NULL) {
+
+ //
+ // Copy the sid
+ //
+
+ RtlMoveMemory( ReturnSid, DomainInfo->DomainSid, (*SidLength) );
+ (*Sid) = ReturnSid;
+ ReturnStatus = TRUE;
+ }
+
+
+ LsaFreeMemory( DomainInfo );
+ }
+
+ Status = LsaClose( PolicyHandle );
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ return(ReturnStatus);
+}
diff --git a/private/lsa/uclient/tlookup.c b/private/lsa/uclient/tlookup.c
new file mode 100644
index 000000000..9aba87ab3
--- /dev/null
+++ b/private/lsa/uclient/tlookup.c
@@ -0,0 +1,249 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ tlookup.c
+
+Abstract:
+
+ Test for a leak in LsaLookupName().
+
+
+ nmake UMTYPE=console UMTEST=tlookup
+
+Author:
+
+ Jim Kelly (JimK) Mar-31-1994
+
+
+Revision History:
+
+--*/
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Includes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include <string.h>
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Global Variables //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+ULONG
+ TLsapLoopLimit = 1000;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Local routine definitions //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+VOID _CRTAPI1
+main(argc, argv)
+int argc;
+char **argv;
+{
+
+ NTSTATUS
+ NtStatus,
+ LookupSidStatus = STATUS_SUCCESS;
+
+ ULONG
+ i;
+
+ LSA_HANDLE
+ PolicyHandle;
+
+ OBJECT_ATTRIBUTES
+ ObjectAttributes;
+
+ UNICODE_STRING
+ NameBuffer,
+ SystemName;
+
+ PUNICODE_STRING
+ Names;
+
+ PLSA_REFERENCED_DOMAIN_LIST
+ ReferencedDomains;
+
+ PLSA_TRANSLATED_SID
+ Sids;
+
+ ULONG
+ SidLength;
+
+ PSID
+ LookupSid;
+
+ BOOLEAN
+ DoSidTest;
+
+ PLSA_TRANSLATED_NAME
+ TranslatedNames;
+
+
+ Names = &NameBuffer;
+ RtlInitUnicodeString( &NameBuffer, L"jimk_dom2\\Jimk_d2_t1" );
+ RtlInitUnicodeString( &SystemName, L"" );
+
+
+ printf("\n\nLsaLookupName() and LsaLookupSid() test.\n"
+ "This test is good to use when looking for leaks\n"
+ "in the lookup paths.\n\n");
+
+
+ //
+ // open the policy object and then loop, looking up names.
+ //
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
+ NtStatus = LsaOpenPolicy(
+ &SystemName,
+ &ObjectAttributes,
+ POLICY_EXECUTE,
+ &PolicyHandle
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Get a sid to lookup
+ //
+
+ ReferencedDomains = NULL;
+ Sids = NULL;
+ NtStatus = LsaLookupNames(
+ PolicyHandle,
+ 1, //Count
+ Names,
+ &ReferencedDomains,
+ &Sids
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Build a sid to use in the lookup
+ // This is done by copying the ReferencedDomain sid,
+ // then adding one more relative ID.
+ //
+
+ ASSERT(ReferencedDomains != NULL);
+ ASSERT(Sids != NULL);
+
+ SidLength =
+ sizeof(ULONG) +
+ RtlLengthSid( &ReferencedDomains->Domains[Sids[0].DomainIndex].Sid );
+
+ LookupSid = RtlAllocateHeap( RtlProcessHeap(), 0, SidLength );
+ ASSERT(LookupSid != NULL);
+
+ RtlCopySid( SidLength,
+ LookupSid,
+ ReferencedDomains->Domains[Sids[0].DomainIndex].Sid );
+ (*RtlSubAuthoritySid(
+ LookupSid,
+ (ULONG)(*RtlSubAuthorityCountSid(LookupSid))
+ )) = Sids[0].RelativeId;
+ (*RtlSubAuthorityCountSid(LookupSid)) += 1;
+
+ DoSidTest = TRUE;
+
+ } else {
+
+ printf("ERROR: Couldn't get SID value to lookup.\n"
+ " Won't perform SID lookup part of test.\n\n");
+ DoSidTest = FALSE;
+ }
+
+ if (ReferencedDomains != NULL) {
+ LsaFreeMemory( ReferencedDomains );
+ }
+ if (Sids != NULL) {
+ LsaFreeMemory( Sids );
+ }
+
+
+ printf("\nLooping %d times...\n", TLsapLoopLimit);
+ for (i=0; i<TLsapLoopLimit; i++) {
+
+ printf("\r Loop Count %#4d LookupName Status: 0x%08lx LookupSid Status: 0x%08lx",
+ i, NtStatus, LookupSidStatus);
+
+ //
+ // Lookup name
+ //
+
+ ReferencedDomains = NULL;
+ Sids = NULL;
+ NtStatus = LsaLookupNames(
+ PolicyHandle,
+ 1, //Count
+ Names,
+ &ReferencedDomains,
+ &Sids
+ );
+
+ if (ReferencedDomains != NULL) {
+ LsaFreeMemory( ReferencedDomains );
+ }
+ if (Sids != NULL) {
+ LsaFreeMemory( Sids );
+ }
+
+
+
+ //
+ // Lookup SID
+ //
+
+ if (DoSidTest) {
+ ReferencedDomains = NULL;
+ Sids = NULL;
+ LookupSidStatus = LsaLookupSids(
+ PolicyHandle,
+ 1, //Count
+ &LookupSid,
+ &ReferencedDomains,
+ &TranslatedNames
+ );
+
+ if (ReferencedDomains != NULL) {
+ LsaFreeMemory( ReferencedDomains );
+ }
+ if (TranslatedNames != NULL) {
+ LsaFreeMemory( TranslatedNames );
+ }
+
+ }
+ }
+ }
+
+ return;
+}
diff --git a/private/lsa/uclient/tsecomm.c b/private/lsa/uclient/tsecomm.c
new file mode 100644
index 000000000..8b6f019c0
--- /dev/null
+++ b/private/lsa/uclient/tsecomm.c
@@ -0,0 +1,136 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tsecomm.c
+
+Abstract:
+
+ Common security definitions and routines.
+
+ This module defines macros that provide a mode-independent
+ interface for security test procedures.
+
+ The mode must be specified by defining one, but not both,
+ of:
+ _TST_USER_ (for user mode tests)
+ _TST_KERNEL_ (for kernel mode tests)
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+
+Environment:
+
+ Test of security.
+
+Revision History:
+
+--*/
+
+#ifndef _TSECOMM_
+#define _TSECOMM_
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Common Definitions //
+// //
+////////////////////////////////////////////////////////////////
+
+#define SEASSERT_SUCCESS(s) { \
+ if (!NT_SUCCESS((s))) { \
+ DbgPrint("** ! Failed ! **\n"); \
+ DbgPrint("Status is: 0x%lx \n", (s)); \
+ } \
+ ASSERT(NT_SUCCESS(s)); }
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Kernel Mode Definitions //
+// //
+////////////////////////////////////////////////////////////////
+
+#ifdef _TST_KERNEL_
+
+#define TstAllocatePool(PoolType,NumberOfBytes) \
+ (ExAllocatePool( (PoolType), (NumberOfBytes) ))
+
+#define TstDeallocatePool(Pointer, NumberOfBytes) \
+ (ExFreePool( (Pointer) ))
+
+#endif // _TST_KERNEL_
+
+
+////////////////////////////////////////////////////////////////
+// //
+// User Mode Definitions //
+// //
+////////////////////////////////////////////////////////////////
+
+
+#ifdef _TST_USER_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+
+// #include "sep.h"
+
+#define TstAllocatePool(IgnoredPoolType,NumberOfBytes) \
+ (ITstAllocatePool( (NumberOfBytes) ))
+
+#define TstDeallocatePool(Pointer, NumberOfBytes) \
+ (ITstDeallocatePool((Pointer),(NumberOfBytes) ))
+
+PVOID
+ITstAllocatePool(
+ IN ULONG NumberOfBytes
+ )
+{
+ NTSTATUS Status;
+ PVOID PoolAddress = NULL;
+ ULONG RegionSize;
+
+ RegionSize = NumberOfBytes;
+
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &PoolAddress,
+ 0,
+ &RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+
+ return PoolAddress;
+}
+
+VOID
+ITstDeallocatePool(
+ IN PVOID Pointer,
+ IN ULONG NumberOfBytes
+ )
+{
+ NTSTATUS Status;
+ PVOID PoolAddress;
+ ULONG RegionSize;
+
+ RegionSize = NumberOfBytes;
+ PoolAddress = Pointer;
+
+ Status = NtFreeVirtualMemory( NtCurrentProcess(),
+ &PoolAddress,
+ &RegionSize,
+ MEM_DECOMMIT
+ );
+
+ return;
+}
+#endif // _TST_USER_
+
+#endif //_TSECOMM_
diff --git a/private/lsa/uclient/tsevars.c b/private/lsa/uclient/tsevars.c
new file mode 100644
index 000000000..babbdf563
--- /dev/null
+++ b/private/lsa/uclient/tsevars.c
@@ -0,0 +1,447 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tsevars.c
+
+Abstract:
+
+ This Module contains variables used in security test routines.
+
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+
+Environment:
+
+ Test.
+
+Revision History:
+
+--*/
+
+#include "tsecomm.c" // Mode dependent macros and routines.
+
+
+#ifndef _TSEVARS_
+#define _TSEVARS_
+
+
+
+
+typedef enum _USERS {
+ Fred,
+ Wilma,
+ Pebbles,
+ Barney,
+ Betty,
+ Bambam,
+ Dino
+} USERS;
+
+
+//
+// Define the Bedrock domain and its inhabitants
+//
+// Bedrock Domain S-1-39824-21-3-17
+// Fred S-1-39824-21-3-17-2
+// Wilma S-1-39824-21-3-17-3
+// Pebbles S-1-39824-21-3-17-4
+// Dino S-1-39824-21-3-17-5
+// Barney S-1-39824-21-3-17-6
+// Betty S-1-39824-21-3-17-7
+// Bambam S-1-39824-21-3-17-8
+// Flintstone S-1-39824-21-3-17-9
+// Rubble S-1-39824-21-3-17-10
+// Adult S-1-39824-21-3-17-11
+// Child S-1-39824-21-3-17-12
+// Neanderthol S-1-39824-21-3-17-13
+//
+
+#define BEDROCK_AUTHORITY {0,0,0,0,155,144}
+
+#define BEDROCKA_AUTHORITY {0,0,0,0,155,145}
+#define BEDROCKB_AUTHORITY {0,0,0,0,155,146}
+#define BEDROCKC_AUTHORITY {0,0,0,0,155,147}
+#define BEDROCKD_AUTHORITY {0,0,0,0,155,148}
+#define BEDROCKE_AUTHORITY {0,0,0,0,155,149}
+
+#define BEDROCK_SUBAUTHORITY_0 0x00000015L
+#define BEDROCK_SUBAUTHORITY_1 0x00000003L
+#define BEDROCK_SUBAUTHORITY_2 0x00000011L
+
+#define BEDROCKA_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKA_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKA_SUBAUTHORITY_2 0x00000111L
+
+#define BEDROCKB_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKB_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKB_SUBAUTHORITY_2 0x00000211L
+
+#define BEDROCKC_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKC_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKC_SUBAUTHORITY_2 0x00000311L
+
+#define BEDROCKD_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKD_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKD_SUBAUTHORITY_2 0x00000411L
+
+#define BEDROCKE_SUBAUTHORITY_0 0x00000015L
+#define BEDROCKE_SUBAUTHORITY_1 0x00000003L
+#define BEDROCKE_SUBAUTHORITY_2 0x00000511L
+
+#define FRED_RID 0x00000002L
+#define WILMA_RID 0x00000003L
+#define PEBBLES_RID 0x00000004L
+#define DINO_RID 0x00000005L
+
+#define BARNEY_RID 0x00000006L
+#define BETTY_RID 0x00000007L
+#define BAMBAM_RID 0x00000008L
+
+#define FLINTSTONE_RID 0x00000009L
+#define RUBBLE_RID 0x0000000AL
+
+#define ADULT_RID 0x0000000BL
+#define CHILD_RID 0x0000000CL
+
+#define NEANDERTHOL_RID 0x0000000DL
+
+
+PSID BedrockDomainSid;
+
+PSID BedrockADomainSid;
+PSID BedrockBDomainSid;
+PSID BedrockCDomainSid;
+PSID BedrockDDomainSid;
+PSID BedrockEDomainSid;
+
+PSID FredSid;
+PSID WilmaSid;
+PSID PebblesSid;
+PSID DinoSid;
+
+PSID BarneySid;
+PSID BettySid;
+PSID BambamSid;
+
+PSID FlintstoneSid;
+PSID RubbleSid;
+
+PSID AdultSid;
+PSID ChildSid;
+
+PSID NeandertholSid;
+
+
+//
+// Universal well known SIDs
+//
+
+PSID NullSid;
+PSID WorldSid;
+PSID LocalSid;
+PSID CreatorOwnerSid;
+PSID CreatorGroupSid;
+
+//
+// Sids defined by NT
+//
+
+PSID NtAuthoritySid;
+
+PSID DialupSid;
+PSID NetworkSid;
+PSID BatchSid;
+PSID InteractiveSid;
+PSID LocalSystemSid;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Define the well known privileges //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+LUID CreateTokenPrivilege;
+LUID AssignPrimaryTokenPrivilege;
+LUID LockMemoryPrivilege;
+LUID IncreaseQuotaPrivilege;
+LUID UnsolicitedInputPrivilege;
+LUID TcbPrivilege;
+LUID SecurityPrivilege;
+
+LUID TakeOwnershipPrivilege;
+LUID LpcReplyBoostPrivilege;
+LUID CreatePagefilePrivilege;
+LUID IncreaseBasePriorityPrivilege;
+LUID SystemProfilePrivilege;
+LUID SystemtimePrivilege;
+LUID ProfileSingleProcessPrivilege;
+
+LUID RestorePrivilege;
+LUID BackupPrivilege;
+LUID CreatePermanentPrivilege;
+LUID ShutdownPrivilege;
+LUID DebugPrivilege;
+
+
+
+
+
+BOOLEAN
+TSeVariableInitialization()
+/*++
+
+Routine Description:
+
+ This function initializes the global variables used in security
+ tests.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if variables successfully initialized.
+ FALSE if not successfully initialized.
+
+--*/
+{
+ ULONG SidWithZeroSubAuthorities;
+ ULONG SidWithOneSubAuthority;
+ ULONG SidWithThreeSubAuthorities;
+ ULONG SidWithFourSubAuthorities;
+
+ SID_IDENTIFIER_AUTHORITY NullSidAuthority = SECURITY_NULL_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY LocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+
+ SID_IDENTIFIER_AUTHORITY BedrockAuthority = BEDROCK_AUTHORITY;
+
+ SID_IDENTIFIER_AUTHORITY BedrockAAuthority = BEDROCKA_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockBAuthority = BEDROCKB_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockCAuthority = BEDROCKC_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockDAuthority = BEDROCKD_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY BedrockEAuthority = BEDROCKE_AUTHORITY;
+
+ //
+ // The following SID sizes need to be allocated
+ //
+
+ SidWithZeroSubAuthorities = RtlLengthRequiredSid( 0 );
+ SidWithOneSubAuthority = RtlLengthRequiredSid( 1 );
+ SidWithThreeSubAuthorities = RtlLengthRequiredSid( 3 );
+ SidWithFourSubAuthorities = RtlLengthRequiredSid( 4 );
+
+ //
+ // Allocate and initialize the universal SIDs
+ //
+
+ NullSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ WorldSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ LocalSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ CreatorOwnerSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ CreatorGroupSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+
+ RtlInitializeSid( NullSid, &NullSidAuthority, 1 );
+ RtlInitializeSid( WorldSid, &WorldSidAuthority, 1 );
+ RtlInitializeSid( LocalSid, &LocalSidAuthority, 1 );
+ RtlInitializeSid( CreatorOwnerSid, &CreatorSidAuthority, 1 );
+ RtlInitializeSid( CreatorGroupSid, &CreatorSidAuthority, 1 );
+
+ *(RtlSubAuthoritySid( NullSid, 0 )) = SECURITY_NULL_RID;
+ *(RtlSubAuthoritySid( WorldSid, 0 )) = SECURITY_WORLD_RID;
+ *(RtlSubAuthoritySid( LocalSid, 0 )) = SECURITY_LOCAL_RID;
+ *(RtlSubAuthoritySid( CreatorOwnerSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
+ *(RtlSubAuthoritySid( CreatorGroupSid, 0 )) = SECURITY_CREATOR_GROUP_RID;
+
+ //
+ // Allocate and initialize the NT defined SIDs
+ //
+
+ NtAuthoritySid = (PSID)TstAllocatePool(PagedPool,SidWithZeroSubAuthorities);
+ DialupSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ NetworkSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ BatchSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ InteractiveSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ LocalSystemSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+
+ RtlInitializeSid( NtAuthoritySid, &NtAuthority, 0 );
+ RtlInitializeSid( DialupSid, &NtAuthority, 1 );
+ RtlInitializeSid( NetworkSid, &NtAuthority, 1 );
+ RtlInitializeSid( BatchSid, &NtAuthority, 1 );
+ RtlInitializeSid( InteractiveSid, &NtAuthority, 1 );
+ RtlInitializeSid( LocalSystemSid, &NtAuthority, 1 );
+
+ *(RtlSubAuthoritySid( DialupSid, 0 )) = SECURITY_DIALUP_RID;
+ *(RtlSubAuthoritySid( NetworkSid, 0 )) = SECURITY_NETWORK_RID;
+ *(RtlSubAuthoritySid( BatchSid, 0 )) = SECURITY_BATCH_RID;
+ *(RtlSubAuthoritySid( InteractiveSid, 0 )) = SECURITY_INTERACTIVE_RID;
+ *(RtlSubAuthoritySid( LocalSystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID;
+
+
+
+ //
+ // Allocate and initialize the Bedrock SIDs
+ //
+
+ BedrockDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockADomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockBDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockCDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockDDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+ BedrockEDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+
+ FredSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ WilmaSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ PebblesSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ DinoSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ BarneySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ BettySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ BambamSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ FlintstoneSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ RubbleSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ AdultSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ ChildSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ NeandertholSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ RtlInitializeSid( BedrockDomainSid, &BedrockAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockDomainSid, 0)) = BEDROCK_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockDomainSid, 1)) = BEDROCK_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockDomainSid, 2)) = BEDROCK_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockADomainSid, &BedrockAAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockADomainSid, 0)) = BEDROCKA_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockADomainSid, 1)) = BEDROCKA_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockADomainSid, 2)) = BEDROCKA_SUBAUTHORITY_2;
+
+
+ RtlInitializeSid( BedrockBDomainSid, &BedrockBAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockBDomainSid, 0)) = BEDROCKB_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockBDomainSid, 1)) = BEDROCKB_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockBDomainSid, 2)) = BEDROCKB_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockCDomainSid, &BedrockCAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockCDomainSid, 0)) = BEDROCKC_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockCDomainSid, 1)) = BEDROCKC_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockCDomainSid, 2)) = BEDROCKC_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockDDomainSid, &BedrockDAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockDDomainSid, 0)) = BEDROCKD_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockDDomainSid, 1)) = BEDROCKD_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockDDomainSid, 2)) = BEDROCKD_SUBAUTHORITY_2;
+
+ RtlInitializeSid( BedrockEDomainSid, &BedrockEAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockEDomainSid, 0)) = BEDROCKE_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockEDomainSid, 1)) = BEDROCKE_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockEDomainSid, 2)) = BEDROCKE_SUBAUTHORITY_2;
+
+ RtlCopySid( SidWithFourSubAuthorities, FredSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( FredSid )) += 1;
+ *(RtlSubAuthoritySid( FredSid, 3)) = FRED_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, WilmaSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( WilmaSid )) += 1;
+ *(RtlSubAuthoritySid( WilmaSid, 3)) = WILMA_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, PebblesSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( PebblesSid )) += 1;
+ *(RtlSubAuthoritySid( PebblesSid, 3)) = PEBBLES_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, DinoSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( DinoSid )) += 1;
+ *(RtlSubAuthoritySid( DinoSid, 3)) = DINO_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BarneySid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BarneySid )) += 1;
+ *(RtlSubAuthoritySid( BarneySid, 3)) = BARNEY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BettySid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BettySid )) += 1;
+ *(RtlSubAuthoritySid( BettySid, 3)) = BETTY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BambamSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BambamSid )) += 1;
+ *(RtlSubAuthoritySid( BambamSid, 3)) = BAMBAM_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, FlintstoneSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( FlintstoneSid )) += 1;
+ *(RtlSubAuthoritySid( FlintstoneSid, 3)) = FLINTSTONE_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, RubbleSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( RubbleSid )) += 1;
+ *(RtlSubAuthoritySid( RubbleSid, 3)) = RUBBLE_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, AdultSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( AdultSid )) += 1;
+ *(RtlSubAuthoritySid( AdultSid, 3)) = ADULT_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, ChildSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( ChildSid )) += 1;
+ *(RtlSubAuthoritySid( ChildSid, 3)) = CHILD_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, NeandertholSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( NeandertholSid )) += 1;
+ *(RtlSubAuthoritySid( NeandertholSid, 3)) = NEANDERTHOL_RID;
+
+ CreateTokenPrivilege =
+ RtlConvertLongToLargeInteger(SE_CREATE_TOKEN_PRIVILEGE);
+ AssignPrimaryTokenPrivilege =
+ RtlConvertLongToLargeInteger(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE);
+ LockMemoryPrivilege =
+ RtlConvertLongToLargeInteger(SE_LOCK_MEMORY_PRIVILEGE);
+ IncreaseQuotaPrivilege =
+ RtlConvertLongToLargeInteger(SE_INCREASE_QUOTA_PRIVILEGE);
+ UnsolicitedInputPrivilege =
+ RtlConvertLongToLargeInteger(SE_UNSOLICITED_INPUT_PRIVILEGE);
+ TcbPrivilege =
+ RtlConvertLongToLargeInteger(SE_TCB_PRIVILEGE);
+ SecurityPrivilege =
+ RtlConvertLongToLargeInteger(SE_SECURITY_PRIVILEGE);
+ TakeOwnershipPrivilege =
+ RtlConvertLongToLargeInteger(SE_TAKE_OWNERSHIP_PRIVILEGE);
+ LpcReplyBoostPrivilege =
+ RtlConvertLongToLargeInteger(SE_LPC_REPLY_BOOST_PRIVILEGE);
+ CreatePagefilePrivilege =
+ RtlConvertLongToLargeInteger(SE_CREATE_PAGEFILE_PRIVILEGE);
+ IncreaseBasePriorityPrivilege =
+ RtlConvertLongToLargeInteger(SE_INC_BASE_PRIORITY_PRIVILEGE);
+ SystemProfilePrivilege =
+ RtlConvertLongToLargeInteger(SE_SYSTEM_PROFILE_PRIVILEGE);
+ SystemtimePrivilege =
+ RtlConvertLongToLargeInteger(SE_SYSTEMTIME_PRIVILEGE);
+ ProfileSingleProcessPrivilege =
+ RtlConvertLongToLargeInteger(SE_PROF_SINGLE_PROCESS_PRIVILEGE);
+ CreatePermanentPrivilege =
+ RtlConvertLongToLargeInteger(SE_CREATE_PERMANENT_PRIVILEGE);
+ BackupPrivilege =
+ RtlConvertLongToLargeInteger(SE_BACKUP_PRIVILEGE);
+ RestorePrivilege =
+ RtlConvertLongToLargeInteger(SE_RESTORE_PRIVILEGE);
+ ShutdownPrivilege =
+ RtlConvertLongToLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+ DebugPrivilege =
+ RtlConvertLongToLargeInteger(SE_DEBUG_PRIVILEGE);
+
+
+ return TRUE;
+
+}
+#endif // _TSEVARS_