summaryrefslogtreecommitdiffstats
path: root/private/net/svcdlls/fpnw/client
diff options
context:
space:
mode:
Diffstat (limited to 'private/net/svcdlls/fpnw/client')
-rw-r--r--private/net/svcdlls/fpnw/client/encrypt.c314
-rw-r--r--private/net/svcdlls/fpnw/client/fpnwclnt.def56
-rw-r--r--private/net/svcdlls/fpnw/client/fpnwclnt.rc10
-rw-r--r--private/net/svcdlls/fpnw/client/logon.c1131
-rw-r--r--private/net/svcdlls/fpnw/client/makefile6
-rw-r--r--private/net/svcdlls/fpnw/client/makefile.inc1
-rw-r--r--private/net/svcdlls/fpnw/client/ncpbind.c117
-rw-r--r--private/net/svcdlls/fpnw/client/ncpstub.c1435
-rw-r--r--private/net/svcdlls/fpnw/client/notify.c543
-rw-r--r--private/net/svcdlls/fpnw/client/nwsutil.c213
-rw-r--r--private/net/svcdlls/fpnw/client/sources69
-rw-r--r--private/net/svcdlls/fpnw/client/usrprop.c831
12 files changed, 4726 insertions, 0 deletions
diff --git a/private/net/svcdlls/fpnw/client/encrypt.c b/private/net/svcdlls/fpnw/client/encrypt.c
new file mode 100644
index 000000000..eefa57237
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/encrypt.c
@@ -0,0 +1,314 @@
+
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ encrypt.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to mangle an objectid, challenge key and
+ password such that a NetWare server will accept the
+ password as valid.
+
+ This program uses information published in Byte Magazine.
+
+Author:
+
+ Colin Watson [ColinW] 15-Mar-1993
+ Andy Herron [AndyHe]
+
+Revision History:
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <nwsutil.h>
+#include <usrprop.h>
+#include <crypt.h>
+#include <fpnwcomm.h>
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Truncate all trailing zeros from the password.
+ //
+
+ while (iPasswordLen > 0 && szUpperPassword[iPasswordLen-1] == 0 ) {
+ iPasswordLen--;
+ }
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
+NTSTATUS
+ReturnNetwareForm(
+ const char * pszSecretValue,
+ DWORD dwUserId,
+ const WCHAR * pchNWPassword,
+ UCHAR * pchEncryptedNWPassword
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and encrypts it with the user
+ supplied password to develop a credential for the intermediate form.
+
+Arguments:
+ DWORD dwUserId - Supplies the 4 byte user's object id
+ const WCHAR * pchNWPassword - Supplies the user's password
+
+ UCHAR * pchEncryptedNWPassword - 16 characters where the result goes.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ DWORD dwStatus;
+ DWORD chObjectId = SWAP_OBJECT_ID (dwUserId);
+ UNICODE_STRING uniNWPassword;
+ OEM_STRING oemNWPassword;
+
+ //
+ // shuffle actually uses 32 bytes, not just 16. It only returns 16 though.
+ //
+
+ UCHAR pszShuffledNWPassword[NT_OWF_PASSWORD_LENGTH * 2];
+
+ uniNWPassword.Buffer = (WCHAR *) pchNWPassword;
+ uniNWPassword.Length = lstrlenW (pchNWPassword)*sizeof(WCHAR);
+ uniNWPassword.MaximumLength = uniNWPassword.Length;
+
+ if ((dwStatus = RtlUpcaseUnicodeStringToOemString (&oemNWPassword,
+ &uniNWPassword,
+ TRUE)) == STATUS_SUCCESS)
+ {
+ Shuffle((UCHAR *) &chObjectId, oemNWPassword.Buffer, oemNWPassword.Length, pszShuffledNWPassword);
+
+ // Encrypt with LSA secret.
+ dwStatus = RtlEncryptNtOwfPwdWithUserKey(
+ (PNT_OWF_PASSWORD) pszShuffledNWPassword,
+ (PUSER_SESSION_KEY) pszSecretValue,
+ (PENCRYPTED_NT_OWF_PASSWORD) pchEncryptedNWPassword);
+ }
+
+ return (dwStatus);
+}
diff --git a/private/net/svcdlls/fpnw/client/fpnwclnt.def b/private/net/svcdlls/fpnw/client/fpnwclnt.def
new file mode 100644
index 000000000..551897318
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/fpnwclnt.def
@@ -0,0 +1,56 @@
+LIBRARY FPNWCLNT
+
+DESCRIPTION 'File and Print for Netware Client DLL'
+
+EXPORTS
+
+ NwApiBufferFree
+ NwServerGetInfo
+ NwServerSetInfo
+ NwVolumeAdd
+ NwVolumeDel
+ NwVolumeEnum
+ NwVolumeGetInfo
+ NwVolumeSetInfo
+ NwConnectionEnum
+ NwConnectionDel
+ NwVolumeConnEnum
+ NwFileEnum
+ NwFileClose
+ NwMessageBufferSend
+ NwSetDefaultQueue
+ NwAddPServer
+ NwRemovePServer
+ FpnwApiBufferFree
+ FpnwServerGetInfo
+ FpnwServerSetInfo
+ FpnwVolumeAdd
+ FpnwVolumeDel
+ FpnwVolumeEnum
+ FpnwVolumeGetInfo
+ FpnwVolumeSetInfo
+ FpnwConnectionEnum
+ FpnwConnectionDel
+ FpnwVolumeConnEnum
+ FpnwFileEnum
+ FpnwFileClose
+ FpnwMessageBufferSend
+ FpnwSetDefaultQueue
+ FpnwAddPServer
+ FpnwRemovePServer
+ SetUserProperty
+ SetUserPropertyWithLength
+ QueryUserProperty
+ QueryUserPropertyWithLength
+ IsNetWareInstalled
+ ReturnNetwareForm
+ GetNcpSecretKey
+ GetRemoteNcpSecretKey
+ Shuffle
+ Msv1_0SubAuthenticationRoutine
+ Msv1_0SubAuthenticationRoutine2
+ MapRidToObjectId
+ SwapObjectId
+ PasswordChangeNotify
+ DeltaNotify
+ InitializeChangeNotify
diff --git a/private/net/svcdlls/fpnw/client/fpnwclnt.rc b/private/net/svcdlls/fpnw/client/fpnwclnt.rc
new file mode 100644
index 000000000..574877690
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/fpnwclnt.rc
@@ -0,0 +1,10 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "FPNW Client DLL"
+#define VER_INTERNALNAME_STR "fpnwclnt.dll"
+
+#include "common.ver"
diff --git a/private/net/svcdlls/fpnw/client/logon.c b/private/net/svcdlls/fpnw/client/logon.c
new file mode 100644
index 000000000..ca5204fba
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/logon.c
@@ -0,0 +1,1131 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+
+Module Name:
+
+ logon.c
+
+Abstract:
+
+ This module contains the routines called by MSV1_0 authentication package.
+
+Author:
+
+ Yi-Hsin Sung (yihsins)
+ Andy Herron (andyhe) 06-Jun-1994 Added support for MSV1_0 subauthority
+
+Revision History:
+
+ Andy Herron (andyhe) 15-Aug-1994 Pulled out (older) unused MSV1_0
+ subauthority routines.
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntsam.h>
+#include <windows.h>
+#include <ntmsv1_0.h>
+#include <crypt.h>
+#include <usrprop.h>
+#include <samrpc.h>
+#include <samisrv.h>
+#include <ntlsa.h>
+#include <lsarpc.h>
+#include <lsaisrv.h>
+#include <lmcons.h>
+#include <logonmsv.h>
+
+#define RESPONSE_SIZE 8
+#define WKSTA_ADDRESS_SIZE 20
+#define NET_ADDRESS_SIZE 8
+#define NODE_ADDRESS_SIZE 12
+
+#define MSV1_0_PASSTHRU 0x01
+#define MSV1_0_GUEST_LOGON 0x02
+
+#ifndef LOGON_SUBAUTH_SESSION_KEY
+#define LOGON_SUBAUTH_SESSION_KEY 0x40
+#endif
+
+ULONG
+MapRidToObjectId(
+ DWORD dwRid,
+ LPWSTR pszUserName,
+ BOOL fNTAS,
+ BOOL fBuiltin );
+
+//
+// These are never closed once they're opened. This is similar to how
+// msv1_0 does it. Since there's no callback at shutdown time, we have no
+// way of knowing when to close them.
+//
+
+HANDLE SamDomainHandle = NULL;
+SAMPR_HANDLE SamConnectHandle = NULL;
+LSA_HANDLE LsaPolicyHandle = NULL;
+
+//
+// This is where we store out LSA Secret
+//
+
+BOOLEAN GotSecret = FALSE;
+UCHAR LsaSecretBuffer[USER_SESSION_KEY_LENGTH + 1];
+
+//
+// forward declare
+//
+
+BOOLEAN
+MNSWorkstationValidate (
+ IN PUNICODE_STRING Workstation,
+ IN PUNICODE_STRING UserParameters
+ );
+
+BOOL
+GetPasswordExpired(
+ IN LARGE_INTEGER PasswordLastSet,
+ IN LARGE_INTEGER MaxPasswordAge
+ );
+
+NTSTATUS
+QueryDomainPasswordInfo (
+ PSAMPR_DOMAIN_INFO_BUFFER *DomainInfo
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+NTSTATUS GetNcpSecretKey( CHAR *pchNWSecretKey );
+
+
+NTSTATUS
+Msv1_0SubAuthenticationRoutine2 (
+ 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,
+ OUT PUSER_SESSION_KEY UserSessionKey OPTIONAL
+)
+/*++
+
+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
+ LOGON_GRACE_LOGON -- The caller's password has expired but logon
+ was allowed during a grace period following the expiration.
+ LOGON_SUBAUTH_SESSION_KEY - a session key was returned from this
+ logon
+
+ 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.
+
+ UserSessionKey - If non-null, recives a session key for this logon
+ session.
+
+
+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;
+ WCHAR PropertyFlag;
+ NT_OWF_PASSWORD DecryptedPassword;
+ UCHAR Response[RESPONSE_SIZE];
+ UNICODE_STRING EncryptedPassword;
+ UNICODE_STRING PasswordDateSet;
+ UNICODE_STRING GraceLoginRemaining;
+ SAMPR_HANDLE UserHandle;
+ LARGE_INTEGER pwSetTime;
+ PSAMPR_DOMAIN_INFO_BUFFER DomainInfo;
+ PSAMPR_USER_INFO_BUFFER userControlInfo;
+ LPWSTR pNewUserParams;
+ int index;
+ UCHAR achK[32];
+ PNETLOGON_NETWORK_INFO LogonNetworkInfo;
+ PCHAR challenge;
+ BOOLEAN authoritative = TRUE; // important default!
+ ULONG userFlags = 0; // important default!
+ ULONG whichFields = 0; // important default!
+ LARGE_INTEGER logoffTime;
+ LARGE_INTEGER kickoffTime;
+
+ pNewUserParams = NULL;
+ DomainInfo = NULL;
+ GraceLoginRemaining.Buffer = NULL;
+ PasswordDateSet.Buffer = NULL;
+ EncryptedPassword.Buffer = NULL;
+ userControlInfo = NULL;
+
+ logoffTime.HighPart = 0x7FFFFFFF; // default to no kickoff and
+ logoffTime.LowPart = 0xFFFFFFFF; // no forced logoff
+ kickoffTime.HighPart = 0x7FFFFFFF;
+ kickoffTime.LowPart = 0xFFFFFFFF;
+
+ (VOID) NtQuerySystemTime( &LogonTime );
+
+ //
+ // Check whether the SubAuthentication package supports this type
+ // of logon.
+ //
+
+ if ( LogonLevel != NetlogonNetworkInformation ) {
+
+ //
+ // This SubAuthentication package only supports network logons.
+ //
+
+ status = STATUS_INVALID_INFO_CLASS;
+ goto CleanUp;
+ }
+
+ //
+ // 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;
+
+ //
+ // If the account type isn't allowed,
+ // Treat this as though the User Account doesn't exist.
+ //
+ // This SubAuthentication package doesn't allow guest logons.
+ //
+
+ if ( ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) ||
+ ( 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;
+ }
+ status = STATUS_ACCOUNT_LOCKED_OUT;
+ goto CleanUp;
+ }
+
+ //
+ // Get the encrypted password from the user parms field
+ //
+
+ status = QueryUserPropertyWithLength( &UserAll->Parameters,
+ NWPASSWORD,
+ &PropertyFlag,
+ &EncryptedPassword );
+
+ if ( !NT_SUCCESS( status )) {
+
+ goto CleanUp;
+ }
+
+ //
+ // If the user does not have a netware password, fail the login
+ //
+
+ if ( EncryptedPassword.Length == 0 ) {
+
+ status = STATUS_NO_SUCH_USER;
+ goto CleanUp;
+ }
+
+ //
+ // Read our LSA secret if we haven't already
+ //
+
+ if (! GotSecret) {
+
+ status = GetNcpSecretKey( &LsaSecretBuffer[0] );
+
+ if (! NT_SUCCESS(status)) {
+
+ goto CleanUp;
+ }
+
+ GotSecret = TRUE;
+ }
+
+ //
+ // Decrypt the password with NetwareLsaSecret to get the one way form
+ //
+
+ status = RtlDecryptNtOwfPwdWithUserKey(
+ (PENCRYPTED_NT_OWF_PASSWORD) EncryptedPassword.Buffer,
+ (PUSER_SESSION_KEY) &LsaSecretBuffer[0],
+ &DecryptedPassword );
+
+ if ( !NT_SUCCESS( status )) {
+
+ goto CleanUp;
+ }
+
+ //
+ // Get the response to challenge. We do this by finishing off the
+ // password encryption algorithm here.
+ //
+
+ challenge = (PCHAR) &(LogonNetworkInfo->LmChallenge);
+
+ Shuffle( challenge, (UCHAR *) &(DecryptedPassword.data), 16, &achK[0] );
+ Shuffle( challenge+4, (UCHAR *) &(DecryptedPassword.data), 16, &achK[16] );
+
+ for (index = 0; index < 16; index++) {
+ achK[index] ^= achK[31-index];
+ }
+
+ for (index = 0; index < RESPONSE_SIZE; index++) {
+ Response[index] = achK[index] ^ achK[15-index];
+ }
+
+ if ( memcmp( Response,
+ (PCHAR) (LogonNetworkInfo->LmChallengeResponse.Buffer),
+ RESPONSE_SIZE ) != 0 ) {
+
+ //
+ // 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;
+ }
+
+ //
+ // if the user tried to use a NULL password, don't note this as
+ // a bad password attempt since LOGON.EXE does this by default.
+ // Instead, map it to STATUS_LOGON_FAILURE.
+ //
+
+ {
+ UCHAR pszShuffledNWPassword[NT_OWF_PASSWORD_LENGTH * 2];
+ DWORD chObjectId;
+ NT_PRODUCT_TYPE ProductType;
+ DWORD dwUserId;
+
+ //
+ // first we calculate what the user's Object ID is...
+ //
+
+ RtlGetNtProductType( &ProductType );
+ dwUserId = MapRidToObjectId(
+ UserAll->UserId,
+ UserAll->UserName.Buffer,
+ ProductType == NtProductLanManNt,
+ FALSE );
+ chObjectId = SWAP_OBJECT_ID (dwUserId);
+
+ //
+ // then we calculate the user's password residue with a null
+ // password
+ //
+
+ RtlZeroMemory( &pszShuffledNWPassword, NT_OWF_PASSWORD_LENGTH * 2 );
+
+ Shuffle( (UCHAR *) &chObjectId, NULL, 0, pszShuffledNWPassword );
+
+ //
+ // we then finish off the encryption as we did above for the
+ // password in the user's record.
+ //
+
+ challenge = (PCHAR) &(LogonNetworkInfo->LmChallenge);
+
+ Shuffle( challenge, pszShuffledNWPassword, 16, &achK[0] );
+ Shuffle( challenge+4, pszShuffledNWPassword, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++) {
+ achK[index] ^= achK[31-index];
+ }
+
+ for (index = 0; index < RESPONSE_SIZE; index++) {
+ Response[index] = achK[index] ^ achK[15-index];
+ }
+
+ //
+ // now if the password that the user sent in matches the encrypted
+ // form of the null password, we exit with a generic return code
+ // that won't cause the user's record to be updated. This will
+ // also cause LSA to not wait for 3 seconds to return the error
+ // (which is a good thing in this case).
+ //
+
+ if ( memcmp( Response,
+ (PCHAR) (LogonNetworkInfo->LmChallengeResponse.Buffer),
+ RESPONSE_SIZE ) == 0 ) {
+
+ status = STATUS_LOGON_FAILURE;
+
+ } else {
+
+ status = STATUS_WRONG_PASSWORD;
+ }
+ }
+ goto CleanUp;
+ }
+
+ //
+ // Prevent rest of things from effecting the Administrator user
+ //
+
+ if (UserAll->UserId == DOMAIN_USER_RID_ADMIN) {
+
+ status = STATUS_SUCCESS;
+ goto CleanUp;
+ }
+
+ //
+ // 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 ) {
+
+ status = STATUS_ACCOUNT_EXPIRED;
+ goto CleanUp;
+ }
+
+ status = QueryDomainPasswordInfo( &DomainInfo );
+
+ if ( !NT_SUCCESS( status )) {
+
+ goto CleanUp;
+ }
+
+ //
+ // Response is correct. So, check if the password has expired or not
+ //
+
+ if (! (UserAll->UserAccountControl & USER_DONT_EXPIRE_PASSWORD)) {
+
+ status = QueryUserPropertyWithLength( &UserAll->Parameters,
+ NWTIMEPASSWORDSET,
+ &PropertyFlag,
+ &PasswordDateSet );
+ if ( !NT_SUCCESS( status ) ||
+ PasswordDateSet.Length < sizeof(LARGE_INTEGER) ) {
+
+ // date last password was set was not present.... hmmm.
+ // we won't update anything here but let someone know all
+ // is not kosher by making this a grace login.
+
+ userFlags = LOGON_GRACE_LOGON;
+
+ } else {
+
+ pwSetTime = *((PLARGE_INTEGER)(PasswordDateSet.Buffer));
+
+ if ( (pwSetTime.HighPart == 0xFFFF &&
+ pwSetTime.LowPart == 0xFFFF ) ||
+ GetPasswordExpired( pwSetTime,
+ DomainInfo->Password.MaxPasswordAge )) {
+
+ //
+ // Password has expired, so check if grace login is still allowed
+ //
+
+ userFlags = LOGON_GRACE_LOGON;
+
+ //
+ // if this is a password validate rather than an
+ // actual login, don't update/check grace logins.
+ //
+
+ if ( LogonNetworkInfo->Identity.Workstation.Length > 0 ) {
+
+ status = QueryUserPropertyWithLength( &UserAll->Parameters,
+ GRACELOGINREMAINING,
+ &PropertyFlag,
+ &GraceLoginRemaining );
+
+ if ( ! NT_SUCCESS( status ) ) {
+
+ //
+ // The grace login value cannot be determined.
+ //
+
+ goto CleanUp;
+
+ } else if ( ( GraceLoginRemaining.Length != 0 ) &&
+ ( *(GraceLoginRemaining.Buffer) > 0 ) ) {
+
+ //
+ // Password has expired and grace login is available.
+ // So, return success and decrease the grace login remaining
+ // in the user parms field.
+ //
+
+ BOOL fUpdate;
+
+ (*(GraceLoginRemaining.Buffer))--;
+
+ status = SetUserProperty( UserAll->Parameters.Buffer,
+ GRACELOGINREMAINING,
+ GraceLoginRemaining,
+ USER_PROPERTY_TYPE_ITEM,
+ &pNewUserParams,
+ &fUpdate );
+
+ if ( NT_SUCCESS( status) &&
+ fUpdate ) {
+
+ //
+ // if we actually updated the parms, mark as such.
+ //
+
+ whichFields = USER_ALL_PARAMETERS;
+
+ //
+ // The length of the parameters didn't grow... we
+ // know that because we started with a value and
+ // ended with the same value - 1 ( same length )
+ //
+
+ memcpy( UserAll->Parameters.Buffer,
+ pNewUserParams,
+ UserAll->Parameters.Length );
+ }
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ status = STATUS_PASSWORD_EXPIRED;
+ goto CleanUp;
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // To validate the user's logon hours, we must have a handle to the user.
+ // We'll open the user here.
+ //
+
+ UserHandle = NULL;
+
+ status = SamrOpenUser( SamDomainHandle,
+ USER_READ_ACCOUNT,
+ UserAll->UserId,
+ &UserHandle );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ KdPrint(( "FPNWCLNT: Cannot SamrOpenUser %lX\n", status));
+ goto CleanUp;
+ }
+
+ //
+ // Validate the user's logon hours.
+ //
+
+ status = SamIAccountRestrictions( UserHandle,
+ NULL, // workstation id
+ NULL, // workstation list
+ &UserAll->LogonHours,
+ &logoffTime,
+ &kickoffTime
+ );
+ SamrCloseHandle( &UserHandle );
+
+ if ( ! NT_SUCCESS( status )) {
+ goto CleanUp;
+ }
+
+ //
+ // Validate if the user can logon from this workstation.
+ // (Supply subauthentication package specific code here.)
+
+ if ( ! MNSWorkstationValidate( &LogonNetworkInfo->Identity.Workstation,
+ &UserAll->Parameters ) ) {
+
+ status = STATUS_INVALID_WORKSTATION;
+ goto CleanUp;
+ }
+
+ //
+ // The user is valid. CleanUp up before returning.
+ //
+
+CleanUp:
+
+ //
+ // If we succeeded, create a session key. The session key is created
+ // by taking the decrypted password (a hash of the object id and
+ // cleartext password) and adding the index of each byte to each byte
+ // modulo 255, and using that to create a new challenge response from
+ // the old challenge response.
+ //
+
+ if (NT_SUCCESS(status) && (UserSessionKey != NULL)) {
+ UCHAR ChallengeResponse[NT_CHALLENGE_LENGTH];
+ PUCHAR Password = (PUCHAR) &DecryptedPassword.data;
+ PUCHAR SessionKey = (PUCHAR) UserSessionKey;
+
+ ASSERT(RESPONSE_SIZE >= NT_CHALLENGE_LENGTH);
+
+ RtlZeroMemory( UserSessionKey, sizeof(*UserSessionKey) );
+
+ RtlCopyMemory(
+ ChallengeResponse,
+ Response,
+ NT_CHALLENGE_LENGTH );
+
+ //
+ // Create the new password
+ //
+
+ for (index = 0; index < sizeof(DecryptedPassword) ; index++ ) {
+ Password[index] = Password[index] + (UCHAR) index;
+ }
+
+ //
+ // Use it to make a normal challenge response using the old challenge
+ // response
+ //
+
+ Shuffle( ChallengeResponse, (UCHAR *) &(DecryptedPassword.data), 16, &achK[0] );
+ Shuffle( ChallengeResponse+4, (UCHAR *) &(DecryptedPassword.data), 16, &achK[16] );
+
+ for (index = 0; index < 16; index++) {
+ achK[index] ^= achK[31-index];
+ }
+
+ for (index = 0; index < RESPONSE_SIZE; index++) {
+ SessionKey[index] = achK[index] ^ achK[15-index];
+ }
+ userFlags |= LOGON_SUBAUTH_SESSION_KEY;
+
+ }
+
+ if (DomainInfo != NULL) {
+ SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainInfo, DomainPasswordInformation );
+ }
+ if (EncryptedPassword.Buffer == NULL) {
+ LocalFree( EncryptedPassword.Buffer );
+ }
+ if (PasswordDateSet.Buffer != NULL) {
+ LocalFree( PasswordDateSet.Buffer );
+ }
+ if (GraceLoginRemaining.Buffer != NULL) {
+ LocalFree( GraceLoginRemaining.Buffer );
+ }
+ if (pNewUserParams != NULL) {
+ LocalFree( pNewUserParams );
+ }
+
+ *Authoritative = authoritative;
+ *UserFlags = userFlags;
+ *WhichFields = whichFields;
+
+ LogoffTime->HighPart = logoffTime.HighPart;
+ LogoffTime->LowPart = logoffTime.LowPart;
+ KickoffTime->HighPart = kickoffTime.HighPart;
+ KickoffTime->LowPart = kickoffTime.LowPart;
+
+ return status;
+
+} // Msv1_0SubAuthenticationRoutine
+
+
+
+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:
+
+ Compatibility wrapper for Msv1_0SubAuthenticationRoutine2.
+
+
+Arguments:
+
+ Same as Msv1_0SubAuthenticationRoutine2
+
+Return Value:
+
+ Same as Msv1_0SubAuthenticationRoutine2
+
+--*/
+{
+ return(Msv1_0SubAuthenticationRoutine2(
+ LogonLevel,
+ LogonInformation,
+ Flags,
+ UserAll,
+ WhichFields,
+ UserFlags,
+ Authoritative,
+ LogoffTime,
+ KickoffTime,
+ NULL // session key
+ ) );
+}
+
+BOOLEAN
+MNSWorkstationValidate (
+ IN PUNICODE_STRING Workstation,
+ IN PUNICODE_STRING UserParameters
+)
+{
+ NTSTATUS status;
+ WCHAR PropertyFlag;
+ UNICODE_STRING LogonWorkstations;
+ INT cbRequired;
+ INT cb;
+ LPWSTR pszTmp;
+
+ if ( Workstation->Length < (NET_ADDRESS_SIZE * sizeof(WCHAR)) ) {
+
+ //
+ // Zero is used when simply verifying a password.
+ //
+ // We also check that the length is enough so we dont
+ // blow up later. If for some reason a bad string is
+ // supplied, we pass it. This should never happen. Not a
+ // security hole as the user has no control over the string.
+ //
+
+ return(TRUE);
+ }
+
+ status = QueryUserPropertyWithLength( UserParameters,
+ NWLOGONFROM,
+ &PropertyFlag,
+ &LogonWorkstations );
+
+ if ( !NT_SUCCESS( status) || LogonWorkstations.Length == 0 ) {
+ return TRUE;
+ }
+
+ cbRequired = (LogonWorkstations.Length + 1) * sizeof(WCHAR);
+ pszTmp = LocalAlloc( LMEM_ZEROINIT, cbRequired);
+
+ if ( pszTmp == NULL ) {
+
+ //
+ // Not enough memory to allocate the buffer. Just
+ // let the user logon.
+ //
+
+ LocalFree( LogonWorkstations.Buffer );
+ return TRUE;
+ }
+
+ cb = MultiByteToWideChar( CP_ACP,
+ MB_PRECOMPOSED,
+ (const CHAR *) LogonWorkstations.Buffer,
+ LogonWorkstations.Length,
+ pszTmp,
+ cbRequired );
+
+ LocalFree( LogonWorkstations.Buffer ); // Don't need it any more
+
+ if ( cb > 1 )
+ {
+ USHORT TotalEntries = LogonWorkstations.Length/WKSTA_ADDRESS_SIZE;
+ WCHAR *pszEntry = pszTmp;
+ WCHAR *pszWksta = Workstation->Buffer ;
+
+ _wcsupr(pszEntry) ;
+ _wcsupr(pszWksta) ;
+
+ while ( TotalEntries > 0 )
+ {
+
+ //
+ // if net # is not wildcard, check for match
+ //
+ if (wcsncmp(L"FFFFFFFF", pszEntry, NET_ADDRESS_SIZE)!=0)
+ {
+ if (wcsncmp(pszWksta, pszEntry, NET_ADDRESS_SIZE)!=0)
+ {
+ //
+ // if no match, goto next entry
+ //
+ pszEntry += WKSTA_ADDRESS_SIZE;
+ TotalEntries--;
+ continue ;
+ }
+ }
+
+ //
+ // from above, net number passes. check node number.
+ // again, look for wildcard first.
+ //
+ if (wcsncmp(L"FFFFFFFFFFFF", pszEntry+NET_ADDRESS_SIZE,
+ NODE_ADDRESS_SIZE)!=0)
+ {
+ if (wcsncmp(pszEntry+NET_ADDRESS_SIZE,
+ pszWksta+NET_ADDRESS_SIZE,
+ NODE_ADDRESS_SIZE)!=0)
+ {
+ //
+ // if no match, goto next entry
+ //
+ pszEntry += WKSTA_ADDRESS_SIZE;
+ TotalEntries--;
+ continue ;
+ }
+ }
+
+ //
+ // found a match. return it.
+ //
+ LocalFree( pszTmp );
+ return TRUE;
+ }
+ } else {
+
+ //
+ // MultiByteToWideChar failed or empty string (ie. 1 char).
+ // Just let the user logon
+ //
+ LocalFree( pszTmp );
+ return TRUE;
+ }
+
+ LocalFree( pszTmp );
+ return FALSE;
+}
+
+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 = NtQuerySystemTime( &TimeNow );
+ if (NT_SUCCESS(status)) {
+
+ if ( RtlLargeIntegerGreaterThanOrEqualTo( TimeNow,
+ PasswordMustChange ) ) {
+ rc = TRUE;
+
+ } else {
+
+ rc = FALSE;
+ }
+ } else {
+ rc = FALSE; // won't fail if NtQuerySystemTime failed.
+ }
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ rc = TRUE;
+ }
+ }
+
+ return rc;
+}
+
+NTSTATUS
+QueryDomainPasswordInfo (
+ PSAMPR_DOMAIN_INFO_BUFFER *DomainInfo
+ )
+/*++
+
+ This routine opens a handle to sam so that we can get the max password
+ age.
+
+--*/
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES PolicyObjectAttributes;
+ PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
+
+ //
+ // if we don't yet have a domain handle, open domain handle so that
+ // we can query the domain's password expiration time.
+ //
+
+ if (SamDomainHandle == NULL) {
+
+ //
+ // Determine the DomainName and DomainId of the Account Database
+ //
+
+ if (LsaPolicyHandle == NULL) {
+
+ InitializeObjectAttributes( &PolicyObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ status = LsaIOpenPolicyTrusted(&LsaPolicyHandle);
+
+ if ( !NT_SUCCESS(status) ) {
+
+ LsaPolicyHandle = NULL;
+ KdPrint(( "FPNWCLNT: Cannot LsaIOpenPolicyTrusted 0x%x\n", status));
+ goto CleanUp;
+ }
+ }
+
+ status = LsarQueryInformationPolicy( LsaPolicyHandle,
+ PolicyAccountDomainInformation,
+ &PolicyAccountDomainInfo );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ KdPrint(( "FPNWCLNT: Cannot LsarQueryInformationPolicy 0x%x\n", status));
+ goto CleanUp;
+ }
+
+ if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
+
+ status = STATUS_NO_SUCH_DOMAIN;
+
+ KdPrint(( "FPNWCLNT: Domain Sid is null 0x%x\n", status));
+ goto CleanUp;
+ }
+
+ //
+ // Open our connection with SAM
+ //
+
+ if (SamConnectHandle == NULL) {
+
+ status = SamIConnect( NULL, // No server name
+ &SamConnectHandle,
+ SAM_SERVER_CONNECT,
+ (BOOLEAN) TRUE ); // Indicate we are privileged
+
+ if ( !NT_SUCCESS(status) ) {
+
+ SamConnectHandle = NULL;
+
+ KdPrint(( "FPNWCLNT: Cannot SamIConnect 0x%x\n", status));
+ goto CleanUp;
+ }
+ }
+
+ //
+ // Open the domain.
+ //
+
+ status = SamrOpenDomain( SamConnectHandle,
+ DOMAIN_READ_OTHER_PARAMETERS,
+ (RPC_SID *) PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
+ &SamDomainHandle );
+
+ if ( !NT_SUCCESS(status) ) {
+
+ SamDomainHandle = NULL;
+ KdPrint(( "FPNWCLNT: Cannot SamrOpenDomain 0x%x\n", status));
+ goto CleanUp;
+ }
+ }
+
+ status = SamrQueryInformationDomain( SamDomainHandle,
+ DomainPasswordInformation,
+ DomainInfo );
+ if ( !NT_SUCCESS(status) ) {
+
+ KdPrint(( "FPNWCLNT: Cannot SamrQueryInformationDomain %lX\n", status));
+ goto CleanUp;
+ }
+
+CleanUp:
+
+ if (PolicyAccountDomainInfo != NULL) {
+ LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAccountDomainInformation,
+ PolicyAccountDomainInfo );
+ }
+ return(status);
+
+} // QueryDomainPasswordInfo
+
+// logon.c eof.
+
diff --git a/private/net/svcdlls/fpnw/client/makefile b/private/net/svcdlls/fpnw/client/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! 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/net/svcdlls/fpnw/client/makefile.inc b/private/net/svcdlls/fpnw/client/makefile.inc
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/makefile.inc
@@ -0,0 +1 @@
+
diff --git a/private/net/svcdlls/fpnw/client/ncpbind.c b/private/net/svcdlls/fpnw/client/ncpbind.c
new file mode 100644
index 000000000..2f04a134c
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/ncpbind.c
@@ -0,0 +1,117 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ncpbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the NcpServer
+ Service.
+
+Author:
+
+ Dan Lafferty (danl) 01-Mar-1991
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 01-Mar-1991 danl
+ created
+ 07-Jun-1991 JohnRo
+ Allowed debug output of failures.
+ 15-Nov-1993 Yi-Hsin
+ Modify for NcpServer.
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nt.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+#include <ncpsvc.h> // generated by the MIDL complier
+#include <ntrpcp.h> // Rpc utils
+#include <srvnames.h> // SERVER_INTERFACE_NAME
+
+
+
+handle_t
+NCPSVC_HANDLE_bind (
+ NCPSVC_HANDLE ServerName
+ )
+
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the ncpserver service client stubs when
+ it is necessary to bind to a 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;
+ RPC_STATUS status;
+
+ status = RpcpBindRpc( ServerName,
+ SERVER_INTERFACE_NAME,
+ // TEXT("Security=Impersonation Static True"),
+ TEXT("Security=Impersonation Dynamic False"),
+ &bindingHandle );
+
+#if DBG
+ if ( status != RPC_S_OK )
+ KdPrint(("NCPSVC_HANDLE_bind: RpcpBindRpc failed status=%lC\n",status));
+#endif
+
+ return( bindingHandle );
+}
+
+
+
+void
+NCPSVC_HANDLE_unbind (
+ NCPSVC_HANDLE ServerName,
+ handle_t BindingHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+ This routine is called from the ncpserver service client stubs when
+ it is necessary to unbind to a 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.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( ServerName ); // This parameter is not used
+
+ RpcpUnbindRpc ( BindingHandle );
+}
diff --git a/private/net/svcdlls/fpnw/client/ncpstub.c b/private/net/svcdlls/fpnw/client/ncpstub.c
new file mode 100644
index 000000000..90a30d086
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/ncpstub.c
@@ -0,0 +1,1435 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ncpstub.c
+
+Abstract:
+
+ Contains NCP Server APIs.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 11-Sept-1993
+ Andy Herron (andyhe)
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <rpc.h>
+#include <ncpsvc.h>
+#include <nwstruct.h>
+
+DWORD NwpMapRpcError(
+ IN DWORD RpcError
+);
+
+
+DWORD
+FpnwApiBufferFree(
+ IN LPVOID pBuffer
+)
+/*++
+
+Routine Description:
+
+ This API frees the memory allocated by all enumeration and getinfo APIs.
+
+Arguments:
+
+ pBuffer - A pointer to an API information buffer previously returned
+ on an API call.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ if ( pBuffer == NULL )
+ return NO_ERROR;
+
+ MIDL_user_free( pBuffer );
+ return NO_ERROR;
+}
+
+DWORD
+NwApiBufferFree(
+ IN LPVOID pBuffer
+)
+{ return(FpnwApiBufferFree( pBuffer ));
+}
+
+
+
+DWORD
+FpnwServerGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppServerInfo
+)
+/*++
+
+Routine Description:
+
+ This API returns the information about the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the server structure requested, use
+ 1 for now.
+
+ ppServerInfo - Place to store a pointer pointing to the returned
+ NWSERVERINFO structure.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ppServerInfo == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrServerGetInfo( pServerName,
+ dwLevel,
+ (PFPNWSERVERINFO *) ppServerInfo );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+
+}
+DWORD
+NwServerGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWSERVERINFO *ppServerInfo
+)
+{ return(FpnwServerGetInfo( pServerName,
+ dwLevel,
+ (LPBYTE *) ppServerInfo));
+}
+
+
+
+DWORD
+FpnwServerSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPBYTE pServerInfo
+)
+/*++
+
+Routine Description:
+
+ This API sets the information about the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the server structure contained in
+ pServerInfo, use 1 for now.
+
+ pServerInfo - Points to a NWSERVERINFO structure which contains the server
+ properties to set to.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( pServerInfo == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrServerSetInfo( pServerName,
+ dwLevel,
+ (PNWSERVERINFO) pServerInfo );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwServerSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN PNWSERVERINFO pServerInfo
+)
+{ return( FpnwServerSetInfo( pServerName,
+ dwLevel,
+ (LPBYTE) pServerInfo ));
+}
+
+
+
+DWORD
+FpnwVolumeAdd(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPBYTE pVolumeInfo
+)
+/*++
+
+Routine Description:
+
+ This API adds a volume to the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ pVolumeInfo, use 1 & 2 for now.
+
+ pVolumeInfo - Points to a NWVOLUMEINFO structure which contains the
+ information about the volume to be added, i.e. the volume name, path,
+ type, user limit and description. dwCurrentUses will be ignored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+ ULONG SDLength = 0;
+ ULONG oldSDLength;
+ PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR oldFileSecurityDescriptor = NULL;
+ PFPNWVOLUMEINFO_2 volInfo = (PFPNWVOLUMEINFO_2) pVolumeInfo;
+
+ if ( dwLevel != 1 && dwLevel != 2 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( pVolumeInfo == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ if ( dwLevel == 2 ) {
+
+ //
+ // Save this. We need to restore this later.
+ //
+
+ oldFileSecurityDescriptor = volInfo->FileSecurityDescriptor;
+ oldSDLength = volInfo->dwFileSecurityDescriptorLength;
+
+ if ( oldFileSecurityDescriptor != NULL ) {
+
+ if ( !RtlValidSecurityDescriptor( oldFileSecurityDescriptor ) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make a self relative security descriptor for use in the
+ // RPC call..
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ NULL,
+ &SDLength
+ );
+
+ if (err != STATUS_BUFFER_TOO_SMALL) {
+
+ return(ERROR_INVALID_PARAMETER);
+
+ } else {
+
+ fileSecurityDescriptor = MIDL_user_allocate( SDLength );
+
+ if ( fileSecurityDescriptor == NULL) {
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ } else {
+
+ //
+ // make an appropriate self-relative security descriptor
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ (PSECURITY_DESCRIPTOR) fileSecurityDescriptor,
+ &SDLength
+ );
+
+ if ( !NT_SUCCESS(err) ) {
+ MIDL_user_free( fileSecurityDescriptor );
+ return(ERROR_INVALID_PARAMETER);
+ }
+
+ volInfo->FileSecurityDescriptor = fileSecurityDescriptor;
+ volInfo->dwFileSecurityDescriptorLength = SDLength;
+ }
+ }
+
+ } else {
+
+ volInfo->dwFileSecurityDescriptorLength = 0;
+ }
+ }
+
+ err = NwrVolumeAdd( pServerName,
+ dwLevel,
+ (LPVOLUME_INFO) pVolumeInfo );
+
+ if ( fileSecurityDescriptor != NULL ) {
+
+ //
+ // restore old values
+ //
+
+ volInfo->dwFileSecurityDescriptorLength = oldSDLength;
+ volInfo->FileSecurityDescriptor = oldFileSecurityDescriptor;
+ MIDL_user_free( fileSecurityDescriptor );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeAdd(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN PNWVOLUMEINFO pVolumeInfo
+)
+{ return( FpnwVolumeAdd( pServerName, dwLevel, (LPBYTE) pVolumeInfo ));
+}
+
+
+
+DWORD
+FpnwVolumeDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName
+)
+/*++
+
+Routine Description:
+
+ This API deletes a volume from the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pVolumeName - Specifies teh volume name that is to be deleted.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( (pVolumeName == NULL) || (pVolumeName[0] == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeDel( pServerName,
+ pVolumeName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName
+)
+{ return( FpnwVolumeDel( pServerName, pVolumeName ));
+}
+
+
+
+DWORD
+FpnwVolumeEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppVolumeInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates all volumes on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppVolumeInfo, use 1 for now.
+
+ ppVolumeInfo - On return, this will point to an array of NWVOLUMEINFO
+ structures, one for each volume on the server.
+
+ pEntriesRead - On return, this will specify the number of volumes returned
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ FPNWVOLUMEINFO_CONTAINER NwVolumeInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ppVolumeInfo == NULL || pEntriesRead == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ NwVolumeInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeEnum( pServerName,
+ dwLevel,
+ &NwVolumeInfoContainer,
+ resumeHandle );
+
+ *ppVolumeInfo = (LPBYTE) NwVolumeInfoContainer.Buffer;
+
+ if ( NwVolumeInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwVolumeInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWVOLUMEINFO *ppVolumeInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return(FpnwVolumeEnum( pServerName,
+ dwLevel,
+ (LPBYTE *)ppVolumeInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwVolumeGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppVolumeInfo
+)
+/*++
+
+Routine Description:
+
+ This querys the information about the given volume on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pVolumeName - A pointer to a UNICODE string containing the name of the
+ volume we want to get information on.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppVolumeInfo, use 1 for now.
+
+ ppVolumeInfo - On return, this will point to a NWVOLUMEINFO structure
+ which contains information on the given volume on the given server.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if ( dwLevel != 1 && dwLevel != 2 ) {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ if ((pVolumeName == NULL) ||
+ (pVolumeName[0] == 0 ) ||
+ (ppVolumeInfo == NULL) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ *ppVolumeInfo = NULL ;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeGetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPVOLUME_INFO *) ppVolumeInfo );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeGetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ OUT PNWVOLUMEINFO *ppVolumeInfo
+)
+{ return(FpnwVolumeGetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPBYTE *)ppVolumeInfo ));
+}
+
+
+
+DWORD
+FpnwVolumeSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ IN LPBYTE pVolumeInfo
+)
+/*++
+
+Routine Description:
+
+ This sets the information about the given volume on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pVolumeName - A pointer to a UNICODE string containing the name of the
+ volume we want to set information on.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ pVolumeInfo, use 1 for now.
+
+ pVolumeInfo - Points to a NWVOLUMEINFO structure which contains
+ information on the given volume to set to. Only dwMaxUses can be
+ set. All the other fields in this structure will be ignored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+ ULONG SDLength = 0;
+ ULONG oldSDLength;
+ PFPNWVOLUMEINFO_2 volInfo = (PFPNWVOLUMEINFO_2) pVolumeInfo;
+
+ PSECURITY_DESCRIPTOR fileSecurityDescriptor = NULL;
+ PSECURITY_DESCRIPTOR oldFileSecurityDescriptor = NULL;
+
+ if ( dwLevel != 1 && dwLevel != 2 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ((pVolumeName == NULL) ||
+ ( pVolumeName[0] == 0 )) ||
+ ( pVolumeInfo == NULL )
+ ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ RpcTryExcept
+ {
+ if ( dwLevel == 2 ) {
+
+ //
+ // Save this. We need to restore this later.
+ //
+
+ oldFileSecurityDescriptor = volInfo->FileSecurityDescriptor;
+ oldSDLength = volInfo->dwFileSecurityDescriptorLength;
+
+ if ( oldFileSecurityDescriptor != NULL ) {
+
+ if ( !RtlValidSecurityDescriptor( oldFileSecurityDescriptor ) ) {
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Make a self relative security descriptor for use in the
+ // RPC call..
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ NULL,
+ &SDLength
+ );
+
+ if (err != STATUS_BUFFER_TOO_SMALL) {
+
+ return(ERROR_INVALID_PARAMETER);
+
+ } else {
+
+ fileSecurityDescriptor = MIDL_user_allocate( SDLength );
+
+ if ( fileSecurityDescriptor == NULL) {
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ } else {
+
+ //
+ // make an appropriate self-relative security descriptor
+ //
+
+ err = RtlMakeSelfRelativeSD(
+ oldFileSecurityDescriptor,
+ (PSECURITY_DESCRIPTOR) fileSecurityDescriptor,
+ &SDLength
+ );
+
+ if ( !NT_SUCCESS(err) ) {
+ MIDL_user_free( fileSecurityDescriptor );
+ return(ERROR_INVALID_PARAMETER);
+ }
+
+ volInfo->FileSecurityDescriptor = fileSecurityDescriptor;
+ volInfo->dwFileSecurityDescriptorLength = SDLength;
+ }
+ }
+
+ } else {
+
+ volInfo->dwFileSecurityDescriptorLength = 0;
+ }
+ }
+
+ err = NwrVolumeSetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPVOLUME_INFO) pVolumeInfo );
+
+ if ( fileSecurityDescriptor != NULL ) {
+
+ //
+ // restore old values
+ //
+
+ volInfo->dwFileSecurityDescriptorLength = oldSDLength;
+ volInfo->FileSecurityDescriptor = oldFileSecurityDescriptor;
+ MIDL_user_free( fileSecurityDescriptor );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeSetInfo(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pVolumeName,
+ IN DWORD dwLevel,
+ IN PNWVOLUMEINFO pVolumeInfo
+)
+{ return( FpnwVolumeSetInfo( pServerName,
+ pVolumeName,
+ dwLevel,
+ (LPBYTE) pVolumeInfo ));
+}
+
+
+
+DWORD
+FpnwConnectionEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT LPBYTE *ppConnectionInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates all connections on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppConnectionInfo, use 1 for now.
+
+ ppConnectionInfo - On return, this will point to an array of
+ NWCONNECTIONINFO structures, one for each volume on the server.
+
+ pEntriesRead - On return, this will specify the number of current
+ connecitons.
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ FPNWCONNECTIONINFO_CONTAINER NwConnectionInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if (( ppConnectionInfo == NULL ) || ( pEntriesRead == NULL ))
+ return ERROR_INVALID_PARAMETER;
+
+ NwConnectionInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrConnectionEnum( pServerName,
+ dwLevel,
+ &NwConnectionInfoContainer,
+ resumeHandle );
+
+ *ppConnectionInfo = (LPBYTE) NwConnectionInfoContainer.Buffer;
+
+ if ( NwConnectionInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwConnectionInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwConnectionEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ OUT PNWCONNECTIONINFO *ppConnectionInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return(FpnwConnectionEnum( pServerName,
+ dwLevel,
+ (LPBYTE *) ppConnectionInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwConnectionDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId
+)
+/*++
+
+Routine Description:
+
+ This delete the connection with the given connection id on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwConnectionId - The identification number of the connection to tear down.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept
+ {
+ err = NwrConnectionDel( pServerName,
+ dwConnectionId );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwConnectionDel(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId
+)
+{ return( FpnwConnectionDel( pServerName, dwConnectionId ));
+}
+
+
+
+DWORD
+FpnwVolumeConnEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pVolumeName OPTIONAL,
+ IN DWORD dwConnectionId,
+ OUT LPBYTE *ppVolumeConnInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates all connections to a volume or list all volumes used by
+ a particular connection on the given server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppVolumeConnInfo, use 1 for now.
+
+ pVolumeName - Specifies the volume name which we want to get all opened
+ resources. This must be NULL if dwConnectionId is not 0.
+
+ dwConnectionId - Specifies the connection id on which we want to get all
+ opened resources. This must be 0 if pVolumeName is not NULL.
+
+ ppVolumeConnInfo - On return, this will point to an array of
+ NWVOLUMECONNINFO structures.
+
+ pEntriesRead - On return, this will specify the number of NWVOLUMECONNINFO
+ returned.
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ FPNWVOLUMECONNINFO_CONTAINER NwVolumeConnInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if ( ( dwConnectionId == 0 )
+ && (( pVolumeName == NULL ) || ( *pVolumeName == 0 ))
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if ( ( dwConnectionId != 0 )
+ && (( pVolumeName != NULL) && ( *pVolumeName != 0 ))
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (( ppVolumeConnInfo == NULL ) || ( pEntriesRead == NULL ))
+ return ERROR_INVALID_PARAMETER;
+
+ NwVolumeConnInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrVolumeConnEnum( pServerName,
+ dwLevel,
+ pVolumeName,
+ dwConnectionId,
+ &NwVolumeConnInfoContainer,
+ resumeHandle );
+
+ *ppVolumeConnInfo = (LPBYTE) NwVolumeConnInfoContainer.Buffer;
+
+ if ( NwVolumeConnInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwVolumeConnInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwVolumeConnEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pVolumeName OPTIONAL,
+ IN DWORD dwConnectionId,
+ OUT PNWVOLUMECONNINFO *ppVolumeConnInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return( FpnwVolumeConnEnum( pServerName,
+ dwLevel,
+ pVolumeName,
+ dwConnectionId,
+ (LPBYTE *) ppVolumeConnInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwFileEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pPathName OPTIONAL,
+ OUT LPBYTE *ppFileInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+/*++
+
+Routine Description:
+
+ This enumerates files opened on the server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwLevel - Reserved for the level of the volume structure contained in
+ *ppFileInfo, use 1 for now.
+
+ pPathName - If this is not NULL, this means that we want to filter
+ on the path. We only want entries with this path, i.e., all users that
+ currently opened the file. If this is NULL, then all files that are
+ opened are returned along with the user information.
+
+ ppFileInfo - On return, this will point to an array of NWFILEINFO structures
+
+ pEntriesRead - On return, this will specify the number of NWFILEINFO
+ returned.
+
+ resumeHandle - On return, a resume handle is stored in the DWORD pointed
+ to by resumeHandle, and is used to continue an existing server search.
+ The handle should be zero on the first call and left unchanged for
+ subsequent calls. If the resumeHandle is NULL, then no resume
+ handle is stored.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+ FPNWFILEINFO_CONTAINER NwFileInfoContainer;
+
+ if ( dwLevel != 1 )
+ return ERROR_INVALID_LEVEL;
+
+ if (( ppFileInfo == NULL ) || ( pEntriesRead == NULL ))
+ return ERROR_INVALID_PARAMETER;
+
+ NwFileInfoContainer.Buffer = NULL;
+
+ RpcTryExcept
+ {
+ err = NwrFileEnum( pServerName,
+ dwLevel,
+ pPathName,
+ &NwFileInfoContainer,
+ resumeHandle );
+
+ *ppFileInfo = (LPBYTE) NwFileInfoContainer.Buffer;
+
+ if ( NwFileInfoContainer.Buffer != NULL ) {
+
+ *pEntriesRead = NwFileInfoContainer.EntriesRead;
+
+ } else {
+
+ *pEntriesRead = 0;
+ }
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwFileEnum(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwLevel,
+ IN LPWSTR pPathName OPTIONAL,
+ OUT PNWFILEINFO *ppFileInfo,
+ OUT PDWORD pEntriesRead,
+ IN OUT PDWORD resumeHandle OPTIONAL
+)
+{ return(FpnwFileEnum( pServerName,
+ dwLevel,
+ pPathName,
+ (LPBYTE *) ppFileInfo,
+ pEntriesRead,
+ resumeHandle ));
+}
+
+
+
+DWORD
+FpnwFileClose(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwFileId
+)
+/*++
+
+Routine Description:
+
+ This closes the file with the given identification number.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwFileId - The identification number of the file to close.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept
+ {
+ err = NwrFileClose( pServerName,
+ dwFileId );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwFileClose(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwFileId
+)
+{ return(FpnwFileClose( pServerName, dwFileId ));
+}
+
+
+
+DWORD
+FpnwMessageBufferSend(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId,
+ IN DWORD fConsoleBroadcast,
+ IN LPBYTE pbBuffer,
+ IN DWORD cbBuffer
+)
+/*++
+
+Routine Description:
+
+ This sends the message to the given connection id.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ dwConnectionId - The id of the connection to send message to.
+
+ fConsoleBroadcast - If this is TRUE, that means use console broadcast. If
+ FALSE, use user broadcast.
+
+ pbBuffer - Points to the message buffer to be sent.
+
+ cbBuffer - The size of the pbBuffer in bytes.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pbBuffer == NULL ) || ( cbBuffer == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrMessageBufferSend( pServerName,
+ dwConnectionId,
+ fConsoleBroadcast,
+ pbBuffer,
+ cbBuffer );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwMessageBufferSend(
+ IN LPWSTR pServerName OPTIONAL,
+ IN DWORD dwConnectionId,
+ IN DWORD fConsoleBroadcast,
+ IN LPBYTE pbBuffer,
+ IN DWORD cbBuffer
+)
+{ return( FpnwMessageBufferSend( pServerName,
+ dwConnectionId,
+ fConsoleBroadcast,
+ pbBuffer,
+ cbBuffer ));
+}
+
+
+
+DWORD
+FpnwSetDefaultQueue(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pQueueName
+)
+/*++
+
+Routine Description:
+
+ This sets the default queue on the server.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pQueueName - The name of the queue that will become the default
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pQueueName == NULL ) || ( *pQueueName == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrSetDefaultQueue( pServerName,
+ pQueueName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwSetDefaultQueue(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pQueueName
+)
+{ return(FpnwSetDefaultQueue( pServerName, pQueueName ));
+}
+
+
+
+DWORD
+FpnwAddPServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+/*++
+
+Routine Description:
+
+ This adds a pserver.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pPServerName - The name of the PServer.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pPServerName == NULL ) || ( *pPServerName == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrAddPServer( pServerName,
+ pPServerName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwAddPServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+{ return( FpnwAddPServer( pServerName, pPServerName ));
+}
+
+
+
+DWORD
+FpnwRemovePServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+/*++
+
+Routine Description:
+
+ This removes a pserver.
+
+Arguments:
+
+ pServerName - A pointer to a UNICODE string containing the name of the
+ remote server on which the function is to execute. A NULL pointer
+ or string specifies the local machine.
+
+ pPServerName - The name of the PServer.
+
+Return Value:
+
+ Error.
+
+--*/
+{
+ DWORD err;
+
+ if (( pPServerName == NULL ) || ( *pPServerName == 0 ))
+ return ERROR_INVALID_PARAMETER;
+
+ RpcTryExcept
+ {
+ err = NwrRemovePServer( pServerName,
+ pPServerName );
+ }
+ RpcExcept(1)
+ {
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+DWORD
+NwRemovePServer(
+ IN LPWSTR pServerName OPTIONAL,
+ IN LPWSTR pPServerName
+)
+{ return( FpnwRemovePServer( pServerName, pPServerName ));
+}
+
+
+DWORD NwpMapRpcError(
+ IN DWORD RpcError
+)
+/*++
+
+Routine Description:
+
+ This routine maps the RPC error into a more meaningful windows
+ error for the caller.
+
+Arguments:
+
+ RpcError - Supplies the exception error raised by RPC
+
+Return Value:
+
+ Returns the mapped error.
+
+--*/
+{
+
+ switch (RpcError)
+ {
+ case RPC_S_INVALID_BINDING:
+ case RPC_X_SS_IN_NULL_CONTEXT:
+ case RPC_X_SS_CONTEXT_DAMAGED:
+ case RPC_X_SS_HANDLES_MISMATCH:
+ case ERROR_INVALID_HANDLE:
+ return ERROR_INVALID_HANDLE;
+
+ case RPC_X_NULL_REF_POINTER:
+ return ERROR_INVALID_PARAMETER;
+
+ case STATUS_ACCESS_VIOLATION:
+ return ERROR_INVALID_ADDRESS;
+
+ default:
+ return RpcError;
+ }
+}
+
+// ncpstub.c eof.
+
diff --git a/private/net/svcdlls/fpnw/client/notify.c b/private/net/svcdlls/fpnw/client/notify.c
new file mode 100644
index 000000000..2e2ff3537
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/notify.c
@@ -0,0 +1,543 @@
+/*++
+
+Copyright (c) 1987-1994 Microsoft Corporation
+
+Module Name:
+
+ notify.c
+
+Abstract:
+
+ Sample SubAuthentication Package.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 27-Feb-1995
+
+Revisions:
+
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntsam.h>
+#include <ntlsa.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winuser.h>
+
+#include <lmaccess.h>
+#include <lmapibuf.h>
+
+#include <nwsutil.h>
+#include <usrprop.h>
+
+#define SW_DLL_NAME L"swclnt.dll"
+#define PASSWORD_PROC_NAME "SwPasswordChangeNotify"
+#define NOTIFY_PROC_NAME "SwDeltaChangeNotify"
+#define NO_GRACE_LOGIN_LIMIT 0xFF
+
+typedef DWORD (*PWPROC)( LPWSTR pUserName,
+ ULONG RelativeId,
+ LPWSTR pPassword );
+
+DWORD
+GetNCPLSASecret(
+ VOID
+);
+
+BOOL fTriedToGetSW = FALSE;
+BOOL fTriedToGetNCP = FALSE;
+HINSTANCE hinstSW = NULL;
+PWPROC ProcPasswordChange = NULL;
+PSAM_DELTA_NOTIFICATION_ROUTINE ProcDeltaChange = NULL;
+BOOL fGotSecret = FALSE;
+char szNWSecretValue[NCP_LSA_SECRET_LENGTH] = "";
+
+
+
+NTSTATUS
+PasswordChangeNotify(
+ PUNICODE_STRING UserName,
+ ULONG RelativeId,
+ PUNICODE_STRING Password
+ )
+{
+ DWORD err = NO_ERROR;
+ PUSER_INFO_2 pUserInfo2 = NULL;
+ LPWSTR pszUser = NULL;
+ LPWSTR pszPassword = NULL;
+
+ //
+ // If password is NULL, we can't get the cleartext password. Hence,
+ // ignore this notification. Same for UserName.
+ //
+ if ( (Password == NULL) || (Password->Buffer == NULL) )
+ return STATUS_SUCCESS;
+
+ if ( (UserName == NULL) || (UserName->Buffer == NULL) )
+ return STATUS_SUCCESS;
+
+ //
+ // if neither DSMN nor FPNW are installed, blow out of here as there's
+ // nothing to do.
+ //
+
+ if ( ( fTriedToGetSW && hinstSW == NULL ) &&
+ ( fTriedToGetNCP && fGotSecret == FALSE) )
+ {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Make sure user name and password are null terminated
+ //
+ pszUser = LocalAlloc( LMEM_ZEROINIT, UserName->Length + sizeof(WCHAR));
+
+ if ( pszUser == NULL )
+ return STATUS_NO_MEMORY;
+
+ pszPassword = LocalAlloc( LMEM_ZEROINIT, Password->Length + sizeof(WCHAR));
+
+ if ( pszPassword == NULL )
+ {
+ LocalFree( pszUser );
+ return STATUS_NO_MEMORY;
+ }
+
+ memcpy( pszUser, UserName->Buffer, UserName->Length );
+ memcpy( pszPassword, Password->Buffer, Password->Length );
+ CharUpper( pszPassword );
+
+ //
+ // First, try to change the small world password if it is installed.
+ //
+ if ( !fTriedToGetSW )
+ {
+ hinstSW = LoadLibrary( SW_DLL_NAME );
+ fTriedToGetSW = TRUE;
+ }
+
+ if (( hinstSW != NULL ) && ( ProcPasswordChange == NULL ))
+ {
+ ProcPasswordChange = (PWPROC) GetProcAddress( hinstSW,
+ PASSWORD_PROC_NAME );
+ }
+
+ if ( ProcPasswordChange != NULL )
+ {
+ err = (ProcPasswordChange)( pszUser, RelativeId, pszPassword );
+ }
+
+#if DBG
+ if ( err )
+ {
+ KdPrint(("[FPNWCLNT] SwPasswordChangeNotify of user %ws changing returns %d.\n", pszUser, err ));
+ }
+#endif
+
+ //
+ // we require that the PDC be rebooted after either DSMN or FPNW is
+ // installed anywhere in the domain for the first server... if we
+ // decide we shouldn't require a reboot, change the code such that
+ // it looks for the LSA secret everytime, not just first time through.
+ //
+
+ if ( !fTriedToGetNCP ) {
+
+ fTriedToGetNCP = TRUE;
+
+ //
+ // Get the LSA secret used to encrypt the password
+ //
+ err = GetNCPLSASecret();
+ }
+
+ if ( !fGotSecret ) {
+
+ goto CleanUp;
+ }
+
+ //
+ // Next, change the netware password residue in the user parms field
+ //
+ err = NetUserGetInfo( NULL,
+ pszUser,
+ 2,
+ (LPBYTE *) &pUserInfo2 );
+
+ if ( !err )
+ {
+ WCHAR PropertyFlag;
+ UNICODE_STRING PropertyValue;
+
+ err = RtlNtStatusToDosError(
+ QueryUserProperty( pUserInfo2->usri2_parms,
+ NWPASSWORD,
+ &PropertyFlag,
+ &PropertyValue ));
+
+
+ if ( !err && PropertyValue.Length != 0 )
+ {
+ //
+ // This is a netware-enabled user, we need to store
+ // the new password residue into the user parms
+ //
+
+ NT_PRODUCT_TYPE ProductType;
+ WCHAR szEncryptedNWPassword[NWENCRYPTEDPASSWORDLENGTH];
+ DWORD dwUserId;
+ WORD wGraceLoginRemaining;
+ WORD wGraceLoginAllowed;
+
+ LocalFree( PropertyValue.Buffer );
+
+ //
+ // Get the grace login allowed and remaining value
+ //
+ err = RtlNtStatusToDosError(
+ QueryUserProperty( pUserInfo2->usri2_parms,
+ GRACELOGINREMAINING,
+ &PropertyFlag,
+ &PropertyValue ));
+
+ if ( !err && ( PropertyValue.Length != 0 ))
+ {
+ wGraceLoginRemaining = (WORD) *(PropertyValue.Buffer);
+ LocalFree( PropertyValue.Buffer );
+
+ if ( wGraceLoginRemaining != NO_GRACE_LOGIN_LIMIT )
+ {
+ // If the grace login remaining is not unlimited,
+ // then we need to reset grace login remaining to
+ // the value in grace login allowed. Hence, read the
+ // grace login allowed value.
+
+ err = RtlNtStatusToDosError(
+ QueryUserProperty( pUserInfo2->usri2_parms,
+ GRACELOGINALLOWED,
+ &PropertyFlag,
+ &PropertyValue ));
+
+ if ( !err && ( PropertyValue.Length != 0 ))
+ {
+ wGraceLoginAllowed = (WORD) *(PropertyValue.Buffer);
+ LocalFree( PropertyValue.Buffer );
+ }
+
+ }
+ }
+
+
+ if ( !err )
+ {
+ RtlGetNtProductType( &ProductType );
+
+
+ dwUserId = MapRidToObjectId(
+ RelativeId,
+ pszUser,
+ ProductType == NtProductLanManNt,
+ FALSE );
+
+ err = RtlNtStatusToDosError(
+ ReturnNetwareForm(
+ szNWSecretValue,
+ dwUserId,
+ pszPassword,
+ (UCHAR *) szEncryptedNWPassword ));
+ }
+
+ if ( !err )
+ {
+ LPWSTR pNewUserParms = NULL;
+ BOOL fUpdate;
+ UNICODE_STRING uPropertyValue;
+
+ uPropertyValue.Buffer = szEncryptedNWPassword;
+ uPropertyValue.Length = uPropertyValue.MaximumLength
+ = sizeof( szEncryptedNWPassword );
+
+ err = RtlNtStatusToDosError(
+ SetUserProperty(
+ pUserInfo2->usri2_parms,
+ NWPASSWORD,
+ uPropertyValue,
+ PropertyFlag,
+ &pNewUserParms,
+ &fUpdate ));
+
+ if ( !err && fUpdate )
+ {
+ USER_INFO_1013 userInfo1013;
+ LPWSTR pNewUserParms2 = NULL;
+ LPWSTR pNewUserParms3 = NULL;
+ LARGE_INTEGER currentTime;
+
+ //
+ // Since we're resetting the user's password, let's
+ // also clear the flag saying the password has
+ // expired. We do this by putting the current
+ // time into the NWPasswordSet.
+ //
+
+ NtQuerySystemTime (&currentTime);
+
+ uPropertyValue.Buffer = (PWCHAR) &currentTime;
+ uPropertyValue.Length = sizeof (LARGE_INTEGER);
+ uPropertyValue.MaximumLength = sizeof (LARGE_INTEGER);
+
+ SetUserProperty( pNewUserParms,
+ NWTIMEPASSWORDSET,
+ uPropertyValue,
+ (SHORT) 0, // not a set
+ &pNewUserParms2,
+ &fUpdate );
+
+ if (pNewUserParms2 != NULL) {
+ userInfo1013.usri1013_parms = pNewUserParms2;
+ } else {
+ userInfo1013.usri1013_parms = pNewUserParms;
+ }
+
+ if ( wGraceLoginRemaining != NO_GRACE_LOGIN_LIMIT )
+ {
+ // If the grace login remaining is not unlimited,
+ // then we need to reset grace login remaining to
+ // the value in grace login allowed.
+
+ uPropertyValue.Buffer = (PWCHAR) &wGraceLoginAllowed;
+ uPropertyValue.Length = uPropertyValue.MaximumLength
+ = sizeof(wGraceLoginAllowed);
+
+ SetUserProperty( userInfo1013.usri1013_parms,
+ GRACELOGINREMAINING,
+ uPropertyValue,
+ (SHORT) 0, // not a set
+ &pNewUserParms3,
+ &fUpdate );
+
+ if (pNewUserParms3 != NULL)
+ userInfo1013.usri1013_parms = pNewUserParms3;
+ }
+
+ err = NetUserSetInfo( NULL,
+ pszUser,
+ USER_PARMS_INFOLEVEL,
+ (LPBYTE) &userInfo1013,
+ NULL );
+
+ if (pNewUserParms2 != NULL)
+ LocalFree( pNewUserParms2 );
+
+ if (pNewUserParms3 != NULL)
+ LocalFree( pNewUserParms3 );
+ }
+
+ if ( pNewUserParms != NULL )
+ LocalFree( pNewUserParms );
+ }
+ }
+
+ NetApiBufferFree( pUserInfo2 );
+ }
+
+#if DBG
+ if ( err )
+ {
+ KdPrint(("[FPNWCLNT] Password of user %ws changing returns %d.\n",
+ pszUser, err ));
+ }
+#endif
+
+CleanUp:
+
+ LocalFree( pszUser );
+
+ // Need to clear all memory that contains password
+ memset( pszPassword, 0, Password->Length + sizeof( WCHAR ));
+ LocalFree( pszPassword );
+
+ return STATUS_SUCCESS;
+}
+
+
+BOOLEAN
+InitializeChangeNotify (
+ VOID
+ )
+{
+ DWORD err = NO_ERROR;
+
+ //
+ // First, check to see if small world is installed.
+ //
+ if ( !fTriedToGetSW )
+ {
+ hinstSW = LoadLibrary( SW_DLL_NAME );
+ fTriedToGetSW = TRUE;
+ }
+
+ if (( hinstSW != NULL )) {
+
+ return TRUE;
+ }
+
+ if ( !fTriedToGetNCP ) {
+
+ fTriedToGetNCP = TRUE;
+
+ //
+ // Get the LSA secret used to encrypt the password
+ //
+ err = GetNCPLSASecret();
+ }
+
+ return fGotSecret;
+}
+
+
+
+DWORD
+GetNCPLSASecret(
+ VOID
+)
+{
+ DWORD err;
+ LSA_HANDLE hlsaPolicy;
+ OBJECT_ATTRIBUTES oa;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+ LSA_HANDLE hlsaSecret;
+ UNICODE_STRING uSecretName;
+ UNICODE_STRING *puSecretValue;
+ LARGE_INTEGER lintCurrentSetTime, lintOldSetTime;
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+
+ //
+ // Set up the object attributes prior to opening the LSA.
+ //
+
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+
+ //
+ // The InitializeObjectAttributes macro presently store NULL for
+ // the psqos field, so we must manually copy that
+ // structure for now.
+ //
+
+ oa.SecurityQualityOfService = &sqos;
+
+
+ err = RtlNtStatusToDosError( LsaOpenPolicy( NULL,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsaPolicy ));
+
+ if ( !err )
+ {
+ RtlInitUnicodeString( &uSecretName, NCP_LSA_SECRET_KEY );
+ err = RtlNtStatusToDosError( LsaOpenSecret( hlsaPolicy,
+ &uSecretName,
+ GENERIC_READ,
+ &hlsaSecret ));
+
+ if ( !err )
+ {
+ err = RtlNtStatusToDosError(
+ LsaQuerySecret( hlsaSecret,
+ &puSecretValue,
+ &lintCurrentSetTime,
+ NULL,
+ &lintOldSetTime ));
+
+ if ( !err )
+ {
+ memcpy( szNWSecretValue,
+ puSecretValue->Buffer,
+ NCP_LSA_SECRET_LENGTH );
+
+ fGotSecret = TRUE;
+
+ (VOID) LsaFreeMemory( puSecretValue );
+ }
+
+ (VOID) LsaClose( hlsaSecret );
+
+ }
+
+ (VOID) LsaClose( hlsaPolicy );
+ }
+
+ return err;
+
+}
+
+
+
+NTSTATUS
+DeltaNotify(
+ IN PSID DomainSid,
+ IN SECURITY_DB_DELTA_TYPE DeltaType,
+ IN SECURITY_DB_OBJECT_TYPE ObjectType,
+ IN ULONG ObjectRid,
+ IN PUNICODE_STRING ObjectName OPTIONAL,
+ IN PLARGE_INTEGER ModifiedCount,
+ IN PSAM_DELTA_DATA DeltaData OPTIONAL
+ )
+{
+ NTSTATUS err = NO_ERROR;
+
+ //
+ // Try to notify small world of SAM changes if it is installed.
+ //
+
+ if ( !fTriedToGetSW )
+ {
+ hinstSW = LoadLibrary( SW_DLL_NAME );
+ fTriedToGetSW = TRUE;
+ }
+
+ if ( ( hinstSW != NULL ) && ( ProcDeltaChange == NULL ))
+ {
+ ProcDeltaChange = (PSAM_DELTA_NOTIFICATION_ROUTINE)
+ GetProcAddress( hinstSW, NOTIFY_PROC_NAME );
+ }
+
+ if ( ProcDeltaChange != NULL )
+ {
+ err = (ProcDeltaChange)( DomainSid,
+ DeltaType,
+ ObjectType,
+ ObjectRid,
+ ObjectName,
+ ModifiedCount,
+ DeltaData );
+ }
+
+#if DBG
+ if ( err )
+ {
+ KdPrint(("[FPNWCLNT] SwDeltaChangeNotify of type %d on rid 0x%x returns %d.\n", DeltaType, ObjectRid, err ));
+ }
+#endif
+
+ return(STATUS_SUCCESS);
+}
diff --git a/private/net/svcdlls/fpnw/client/nwsutil.c b/private/net/svcdlls/fpnw/client/nwsutil.c
new file mode 100644
index 000000000..5349cd8d3
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/nwsutil.c
@@ -0,0 +1,213 @@
+/*++
+
+Copyright (c) 1993-1993 Microsoft Corporation
+
+Module Name:
+
+ nwsutil.c
+
+Abstract:
+
+ This module implements IsNetWareInstalled()
+
+Author:
+
+ Congpa You (CongpaY) 02-Dec-1993 Crested
+
+Revision History:
+
+--*/
+
+#include "nt.h"
+#include "ntrtl.h"
+#include "nturtl.h"
+
+#include "windef.h"
+#include "winerror.h"
+#include "winbase.h"
+
+#include "ntlsa.h"
+#include "nwsutil.h"
+#include "crypt.h"
+
+#include <fpnwcomm.h>
+#include <usrprop.h>
+
+NTSTATUS
+GetRemoteNcpSecretKey (
+ PUNICODE_STRING SystemName,
+ CHAR *pchNWSecretKey
+ )
+{
+ //
+ // this function returns the FPNW LSA Secret for the specified domain
+ //
+
+ NTSTATUS ntstatus;
+ OBJECT_ATTRIBUTES ObjAttributes;
+ LSA_HANDLE PolicyHandle = NULL;
+ LSA_HANDLE SecretHandle = NULL;
+ UNICODE_STRING SecretNameString;
+ PUNICODE_STRING punicodeCurrentValue;
+ PUNICODE_STRING punicodeOldValue;
+
+ InitializeObjectAttributes( &ObjAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL );
+
+ ntstatus = LsaOpenPolicy( SystemName,
+ &ObjAttributes,
+ POLICY_CREATE_SECRET,
+ &PolicyHandle );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ return( ntstatus );
+ }
+
+ RtlInitUnicodeString( &SecretNameString, NCP_LSA_SECRET_KEY );
+
+ ntstatus = LsaOpenSecret( PolicyHandle,
+ &SecretNameString,
+ SECRET_QUERY_VALUE,
+ &SecretHandle );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ LsaClose( PolicyHandle );
+ return( ntstatus );
+ }
+
+ //
+ // Do not need the policy handle anymore.
+ //
+
+ LsaClose( PolicyHandle );
+
+ ntstatus = LsaQuerySecret( SecretHandle,
+ &punicodeCurrentValue,
+ NULL,
+ &punicodeOldValue,
+ NULL );
+
+ //
+ // Do not need the secret handle anymore.
+ //
+
+ LsaClose( SecretHandle );
+
+ if ( NT_SUCCESS(ntstatus) && ( punicodeCurrentValue->Buffer != NULL))
+ {
+ memcpy( pchNWSecretKey,
+ punicodeCurrentValue->Buffer,
+ min(punicodeCurrentValue->Length, USER_SESSION_KEY_LENGTH));
+ }
+
+ LsaFreeMemory( punicodeCurrentValue );
+ LsaFreeMemory( punicodeOldValue );
+
+ return( ntstatus );
+}
+
+NTSTATUS
+GetNcpSecretKey (
+ CHAR *pchNWSecretKey
+ )
+{
+ //
+ // simply return the LSA Secret for the local domain
+ //
+
+ return GetRemoteNcpSecretKey( NULL, pchNWSecretKey );
+}
+
+BOOL IsNetWareInstalled( VOID )
+{
+ CHAR pszNWSecretKey[USER_SESSION_KEY_LENGTH];
+
+ return( !NT_SUCCESS( GetNcpSecretKey (pszNWSecretKey))
+ ? FALSE
+ : (pszNWSecretKey[0] != 0));
+}
+
+NTSTATUS InstallNetWare( LPWSTR lpNcpSecretKey )
+{
+ NTSTATUS ntstatus;
+ OBJECT_ATTRIBUTES ObjAttributes;
+ LSA_HANDLE PolicyHandle;
+ LSA_HANDLE SecretHandle;
+ UNICODE_STRING SecretNameString;
+ UNICODE_STRING unicodeCurrentValue;
+ UNICODE_STRING unicodeOldValue;
+
+ InitializeObjectAttributes( &ObjAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL);
+
+ ntstatus = LsaOpenPolicy( NULL,
+ &ObjAttributes,
+ POLICY_CREATE_SECRET,
+ &PolicyHandle );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ return( ntstatus );
+ }
+
+ RtlInitUnicodeString( &SecretNameString, NCP_LSA_SECRET_KEY );
+
+ ntstatus = LsaCreateSecret( PolicyHandle,
+ &SecretNameString,
+ SECRET_SET_VALUE | DELETE,
+ &SecretHandle );
+
+ if ( ntstatus == STATUS_OBJECT_NAME_COLLISION )
+ {
+ ntstatus = LsaOpenSecret( PolicyHandle,
+ &SecretNameString,
+ SECRET_SET_VALUE,
+ &SecretHandle );
+ }
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ RtlInitUnicodeString( &unicodeOldValue, NULL );
+ RtlInitUnicodeString( &unicodeCurrentValue, lpNcpSecretKey );
+
+ ntstatus = LsaSetSecret( SecretHandle,
+ &unicodeCurrentValue,
+ &unicodeOldValue );
+
+ LsaClose( SecretHandle );
+ }
+
+ LsaClose( PolicyHandle );
+
+ return( ntstatus );
+}
+
+ULONG
+MapRidToObjectId(
+ DWORD dwRid,
+ LPWSTR pszUserName,
+ BOOL fNTAS,
+ BOOL fBuiltin )
+{
+ (void) fBuiltin ; // unused for now.
+
+ if (pszUserName && (lstrcmpi(pszUserName, SUPERVISOR_NAME_STRING)==0))
+ return SUPERVISOR_USERID ;
+
+ return ( fNTAS ? (dwRid | 0x10000000) : dwRid ) ;
+}
+
+
+ULONG SwapObjectId( ULONG ulObjectId )
+{
+ return (MAKELONG(HIWORD(ulObjectId),SWAPWORD(LOWORD(ulObjectId)))) ;
+}
+
diff --git a/private/net/svcdlls/fpnw/client/sources b/private/net/svcdlls/fpnw/client/sources
new file mode 100644
index 000000000..e6a237f47
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/sources
@@ -0,0 +1,69 @@
+!IF 0
+
+Copyright (c) 1989-93 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:
+
+ Rita Wong (ritaw) 14-Feb-1993
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP=fpnw
+MINORCOMP=service
+
+TARGETNAME=fpnwclnt
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLBASE = 0x69700000
+
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\kernel32.lib \
+ \nt\public\sdk\lib\*\samsrv.lib \
+ \nt\public\sdk\lib\*\lsasrv.lib \
+ \nt\public\sdk\lib\*\netapi32.lib \
+ \nt\public\sdk\lib\*\rpcrt4.lib \
+ \nt\public\sdk\lib\*\advapi32.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\user32.lib
+
+INCLUDES=.;..\inc;..\..\..\inc;$(BASEDIR)\private\inc
+
+!IFDEF NTDBGFILES
+BINPLACE_FLAGS=-r $(_NTTREE) -p $(BASEDIR)\private\net\svcdlls\fpnw\nwsplace.txt -s $(_NTTREE)\Symbols
+!ELSE
+BINPLACE_FLAGS=-r $(_NTTREE) -p $(BASEDIR)\private\net\svcdlls\fpnw\nwsplace.txt
+!ENDIF
+
+UNICODE=1
+
+SOURCES= \
+ encrypt.c \
+ logon.c \
+ ncpbind.c \
+ ncpstub.c \
+ ncpsvc_c.c \
+ notify.c \
+ nwsutil.c \
+ usrprop.c \
+ fpnwclnt.rc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE
+
+#386_WARNING_LEVEL=-W4
+
+NTTARGETFILE0=fpnwclnt.rc
diff --git a/private/net/svcdlls/fpnw/client/usrprop.c b/private/net/svcdlls/fpnw/client/usrprop.c
new file mode 100644
index 000000000..151aee47c
--- /dev/null
+++ b/private/net/svcdlls/fpnw/client/usrprop.c
@@ -0,0 +1,831 @@
+/*++
+
+Copyright (c) 1993-1993 Microsoft Corporation
+
+Module Name:
+
+ usrprop.c
+
+Abstract:
+
+ This module implements QueryUserProperty() and SetUserProperty()
+ which read and write NetWare Properties to the UserParms field.
+
+Author:
+
+ Andy Herron (andyhe) 24-May-1993
+ Congpa You (CongpaY) 28-Oct-1993 Seperated SetUserProperty() and
+ QueryUserProperty() out from usrprop.c
+ in ncpsrv\svcdlls\ncpsvc\libbind,
+ modified the code and fixed a few
+ existing problems.
+
+Revision History:
+
+--*/
+
+#include "nt.h"
+#include "ntrtl.h"
+#include "nturtl.h"
+#include "ntioapi.h"
+#include "windef.h"
+#include "winbase.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "winuser.h"
+
+#include <fpnwcomm.h>
+#include <usrprop.h>
+
+//
+// All internal (opaque) structures are listed here since no one else
+// needs to reference them.
+//
+
+//
+// The user's Parameter field is mapped out to a structure that contains
+// the backlevel 48 WCHARs for Mac/Ras compatibility plus a new structure
+// that is basically an array of chars that make up a property name plus
+// a property value.
+//
+
+//
+// This is the structure for an individual property. Note that there are
+// no null terminators in this.
+//
+typedef struct _USER_PROPERTY {
+ WCHAR PropertyLength; // length of property name
+ WCHAR ValueLength; // length of property value
+ WCHAR PropertyFlag; // type of property (1 = set, 2 = item)
+ WCHAR Property[1]; // start of property name, followed by value
+} USER_PROPERTY, *PUSER_PROPERTY;
+
+//
+// This is the structure that maps the beginning of the user's Parameters
+// field. It is only separate so that we can do a sizeof() without including
+// the first property, which may or may not be there.
+//
+
+typedef struct _USER_PROPERTIES_HDR {
+ WCHAR BacklevelParms[48]; // RAS & Mac data stored here.
+ WCHAR PropertySignature; // signature that we can look for.
+ WCHAR PropertyCount; // number of properties present.
+} USER_PROPERTIES_HDR, *PUSER_PROPERTIES_HDR;
+
+//
+// This structure maps out the whole of the user's Parameters field when
+// the user properties structure is present and at least one property is
+// defined.
+//
+
+typedef struct _USER_PROPERTIES {
+ USER_PROPERTIES_HDR Header;
+ USER_PROPERTY FirstProperty;
+} USER_PROPERTIES, *PUSER_PROPERTIES;
+
+//
+// forward references
+//
+
+NTSTATUS
+UserPropertyAllocBlock (
+ IN PUNICODE_STRING Existing,
+ IN ULONG DesiredLength,
+ IN OUT PUNICODE_STRING New
+ );
+
+BOOL
+FindUserProperty (
+ PUSER_PROPERTIES UserProperties,
+ LPWSTR Property,
+ PUSER_PROPERTY *pUserProperty,
+ USHORT *pCount
+ );
+
+VOID
+RemoveUserProperty (
+ UNICODE_STRING *puniUserParms,
+ PUSER_PROPERTY UserProperty,
+ USHORT Count,
+ BOOL *Update
+ );
+
+NTSTATUS
+SetUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR *pNewUserParms, // memory has to be freed afer use.
+ OUT BOOL *Update
+ )
+/*
+ This function sets a property field in the user's Parameters field.
+*/
+{
+ NTSTATUS status;
+ UNICODE_STRING uniUserParms;
+ UNICODE_STRING uniNewUserParms;
+ USHORT Count = 0;
+ USHORT PropertyLength;
+ USHORT ValueLength;
+ PUSER_PROPERTIES UserProperties;
+ PUSER_PROPERTY UserProperty;
+ LPWSTR PropertyValueString = NULL;
+ USHORT oldUserParmsLength;
+ INT i;
+ UCHAR *pchValue = NULL;
+
+ // Check if parameters are correct.
+ if (Property == NULL)
+ {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ // Initialize output variables.
+ *Update = FALSE;
+ *pNewUserParms = NULL;
+
+ try {
+
+ oldUserParmsLength = (lstrlenW(UserParms) + 1) * sizeof(WCHAR);
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // if we can't get the length of the current UserParameters, we loose
+ // the whole thing.
+ //
+
+ UserParms = NULL;
+ }
+
+ // Convert UserParms to unicode string.
+ uniUserParms.Buffer = UserParms;
+ uniUserParms.Length = UserParms ? oldUserParmsLength : 0;
+ uniUserParms.MaximumLength = uniUserParms.Length;
+
+ /** Get the length of the property name **/
+
+ PropertyLength = lstrlenW( Property ) * sizeof(WCHAR);
+
+ /** Get the length of the property value **/
+ ValueLength = PropertyValue.Length;
+
+ if (ValueLength != 0)
+ {
+ // Convert property value to asci string so that
+ // if property value is 0, it can be stored correctly.
+
+ PropertyValueString = (LPWSTR) LocalAlloc (LPTR, (ValueLength+1)*sizeof (WCHAR));
+ if (!PropertyValueString)
+ {
+ return(STATUS_NO_MEMORY) ;
+ }
+
+ pchValue = (UCHAR *) PropertyValue.Buffer;
+
+ // BUGBUG. Since wsprint converts 0x00 to 20 30 (20 is
+ // space and 30 is 0), sscanf converts 20 30 to 0. If the
+ // value is uncode string, this convertsion would not
+ // convert back to original value. So if we want to store
+ // some value in the UserParms, we have to pass in ansi
+ // string.
+
+ for (i = 0; i < ValueLength; i++)
+ {
+ wsprintfA ((PCHAR)(PropertyValueString+i), "%02x", *(pchValue+i));
+ }
+
+ *(PropertyValueString+ValueLength) = 0;
+ ValueLength = ValueLength * sizeof (WCHAR);
+ }
+
+ //
+ // check that user has valid property structure , if not, create one
+ //
+
+ if (UserParms != NULL)
+ {
+ Count = oldUserParmsLength;
+ }
+
+ if (Count < sizeof( USER_PROPERTIES))
+ {
+ Count = sizeof( USER_PROPERTIES_HDR ) + sizeof(WCHAR);
+ }
+
+ if (ValueLength > 0)
+ {
+ Count += sizeof( USER_PROPERTY ) + PropertyLength + ValueLength;
+ }
+
+ if (Count > 0x7FFF)
+ {
+ // can't be bigger than 32K of user parms.
+ return (STATUS_BUFFER_OVERFLOW);
+ }
+
+ try {
+
+ status = UserPropertyAllocBlock( &uniUserParms,
+ Count,
+ &uniNewUserParms );
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // if we can't copy the current UserParameters, we loose the whole thing.
+ //
+
+ UserParms = NULL;
+ uniUserParms.Buffer = UserParms;
+ uniUserParms.Length = 0;
+ uniUserParms.MaximumLength = uniUserParms.Length;
+
+ Count = sizeof( USER_PROPERTIES_HDR ) + sizeof(WCHAR);
+ if (ValueLength > 0) {
+ Count += sizeof( USER_PROPERTY ) + PropertyLength + ValueLength;
+ }
+
+ status = UserPropertyAllocBlock( &uniUserParms,
+ Count,
+ &uniNewUserParms );
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ return status;
+ }
+
+ // Make the output pNewUserParms point to uniNewUserPams's buffer
+ // which is the new UserParms string.
+
+ *pNewUserParms = uniNewUserParms.Buffer;
+
+ UserProperties = (PUSER_PROPERTIES) uniNewUserParms.Buffer;
+
+ try {
+
+ if (FindUserProperty (UserProperties,
+ Property,
+ &UserProperty,
+ &Count))
+ {
+ RemoveUserProperty ( &uniNewUserParms,
+ UserProperty,
+ Count,
+ Update);
+ }
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // we have corrupted user parms here... get rid of them.
+ //
+
+ *Update = TRUE;
+
+ if (*pNewUserParms != NULL) {
+ LocalFree( *pNewUserParms );
+ }
+ *pNewUserParms = NULL;
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if ( !NT_SUCCESS(status) ) {
+ return status;
+ }
+
+ //
+ // If the new value of the property is not null, add it.
+ //
+
+ if (ValueLength > 0) {
+
+ try {
+
+ // find the end of the parameters list
+
+ UserProperty = &(UserProperties->FirstProperty);
+
+ for (Count = 1; Count <= UserProperties->Header.PropertyCount; Count++)
+ {
+ UserProperty = (PUSER_PROPERTY)
+ ((LPSTR)((LPSTR) UserProperty +
+ sizeof(USER_PROPERTY) + // length of entry
+ UserProperty->PropertyLength +
+ UserProperty->ValueLength -
+ sizeof(WCHAR))); // for Property[0]
+ }
+
+ //
+ // append it to the end and update length of string
+ //
+
+ UserProperty->PropertyFlag = (PropertyFlag & NCP_SET) ?
+ USER_PROPERTY_TYPE_SET :
+ USER_PROPERTY_TYPE_ITEM;
+
+ UserProperty->PropertyLength = PropertyLength;
+ UserProperty->ValueLength = ValueLength;
+
+ RtlCopyMemory( &(UserProperty->Property[0]),
+ Property,
+ PropertyLength );
+
+ RtlCopyMemory( &(UserProperty->Property[PropertyLength / sizeof(WCHAR)]),
+ PropertyValueString,
+ ValueLength );
+
+ uniNewUserParms.Length +=
+ sizeof(USER_PROPERTY) + // length of entry
+ PropertyLength + // length of property name string
+ ValueLength - // length of value string
+ sizeof(WCHAR); // account for WCHAR Property[1]
+
+ UserProperties->Header.PropertyCount++;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // we have corrupted user parms here... get rid of them.
+ //
+
+ if (*pNewUserParms != NULL) {
+ LocalFree( *pNewUserParms );
+ }
+ *pNewUserParms = NULL;
+ status = STATUS_INVALID_PARAMETER;
+ }
+ *Update = TRUE;
+ }
+
+ // UserParms is already null terminated. We don't need to set the
+ // end of UserParms to be NULL since we zero init the buffer already.
+
+ return( status );
+}
+
+NTSTATUS
+SetUserPropertyWithLength (
+ IN PUNICODE_STRING UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR *pNewUserParms, // memory has to be freed afer use.
+ OUT BOOL *Update
+ )
+/*
+ This function sets a property field in the user's Parameters field.
+*/
+{
+ NTSTATUS status;
+ UNICODE_STRING newUserParms;
+ ULONG length;
+ PWCHAR ptr;
+
+ length = UserParms->Length;
+
+ if (UserParms->MaximumLength < length + sizeof(WCHAR)) {
+
+ //
+ // have to realloc
+ //
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length =
+ newUserParms.MaximumLength =
+ (USHORT) ( length + sizeof(WCHAR) );
+
+ newUserParms.Buffer = LocalAlloc (LPTR, newUserParms.Length);
+
+ if (newUserParms.Buffer == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory( newUserParms.Buffer,
+ UserParms->Buffer,
+ length );
+
+ } else {
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length = (USHORT) length;
+ newUserParms.MaximumLength = UserParms->MaximumLength;
+ }
+
+ //
+ // Slap in null terminator
+ //
+
+ ptr = newUserParms.Buffer + ( length / sizeof(WCHAR) );
+ *ptr = L'\0';
+
+ status = SetUserProperty( newUserParms.Buffer,
+ Property,
+ PropertyValue,
+ PropertyFlag,
+ pNewUserParms,
+ Update );
+
+ if (newUserParms.Buffer != UserParms->Buffer) {
+
+ LocalFree( newUserParms.Buffer );
+ }
+
+ return(status);
+}
+
+#define MAPHEXTODIGIT(x) ( x >= '0' && x <= '9' ? (x-'0') : \
+ x >= 'A' && x <= 'F' ? (x-'A'+10) : \
+ x >= 'a' && x <= 'f' ? (x-'a'+10) : 0 )
+
+
+NTSTATUS
+QueryUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR PropertyName,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ )
+/*
+ This routine returns a user definable property value as it is stored
+ in the user's Parameters field. Note that the RAS/MAC fields are
+ stripped before we start processing user properties.
+*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ USHORT PropertyNameLength;
+ USHORT Count;
+ PUSER_PROPERTY UserProperty;
+ WCHAR *Value;
+ UINT i;
+ CHAR *PropertyValueString = NULL;
+ CHAR *pchValue;
+
+ // Set PropertyValue->Length to 0 initially. If the property is not found
+ // it will still be 0 on exit.
+
+ PropertyValue->Length = 0;
+ PropertyValue->Buffer = NULL;
+
+ try {
+
+ PropertyNameLength = lstrlenW(PropertyName) * sizeof(WCHAR);
+
+ // Check if UserParms have the right structure.
+
+ if (FindUserProperty ((PUSER_PROPERTIES) UserParms,
+ PropertyName,
+ &UserProperty,
+ &Count) ) {
+
+ Value = (LPWSTR)(LPSTR)((LPSTR) &(UserProperty->Property[0]) +
+ PropertyNameLength);
+
+ //
+ // Found the requested property
+ //
+
+ //
+ // Copy the property flag.
+ //
+
+ if (PropertyFlag) {
+ *PropertyFlag = UserProperty->PropertyFlag;
+ }
+
+ // Allocate memory for PropertyValue->Buffer
+
+ PropertyValueString = LocalAlloc ( LPTR, UserProperty->ValueLength+1);
+ PropertyValue->Buffer = LocalAlloc ( LPTR, UserProperty->ValueLength/sizeof(WCHAR));
+
+ //
+ // Make sure the property value length is valid.
+ //
+ if ((PropertyValue->Buffer == NULL) || (PropertyValueString == NULL)) {
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (PropertyValue->Buffer != NULL) {
+ LocalFree( PropertyValue->Buffer );
+ PropertyValue->Buffer = NULL;
+ }
+
+ } else {
+
+ //
+ // Copy the property value to the buffer.
+ //
+
+ RtlCopyMemory( PropertyValueString,
+ Value,
+ UserProperty->ValueLength );
+
+ pchValue = (CHAR *) PropertyValue->Buffer;
+
+ // Convert from value unicode string to value.
+ for (i = 0; i < UserProperty->ValueLength/sizeof(WCHAR) ; i++)
+ {
+ // sscanf will trash memory.
+ // sscanf( PropertyValueString+2*i, "%2x", pchValue+i);
+
+ pchValue[i] = MAPHEXTODIGIT( PropertyValueString[2*i]) * 16 +
+ MAPHEXTODIGIT( PropertyValueString[2*i+1]);
+ }
+
+ PropertyValue->Length = UserProperty->ValueLength/sizeof(WCHAR);
+ PropertyValue->MaximumLength = UserProperty->ValueLength/sizeof(WCHAR);
+ }
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // we have corrupted user parms here... can't return a decent value
+ //
+
+ if (PropertyValue->Buffer != NULL) {
+ LocalFree( PropertyValue->Buffer );
+ PropertyValue->Buffer = NULL;
+ }
+
+ PropertyValue->Length = 0;
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if ( PropertyValueString != NULL ) {
+ LocalFree( PropertyValueString);
+ }
+
+ return status;
+}
+
+NTSTATUS
+QueryUserPropertyWithLength (
+ IN PUNICODE_STRING UserParms,
+ IN LPWSTR PropertyName,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ )
+/*
+ This routine returns a user definable property value as it is stored
+ in the user's Parameters field. Note that the RAS/MAC fields are
+ stripped before we start processing user properties.
+*/
+{
+ NTSTATUS status;
+ UNICODE_STRING newUserParms;
+ ULONG length;
+ PWCHAR ptr;
+
+ length = UserParms->Length;
+
+ if (UserParms->MaximumLength < length + sizeof(WCHAR)) {
+
+ //
+ // have to realloc
+ //
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length =
+ newUserParms.MaximumLength =
+ (USHORT) (length + sizeof(WCHAR) );
+
+ newUserParms.Buffer = LocalAlloc (LPTR, newUserParms.Length);
+
+ if (newUserParms.Buffer == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory( newUserParms.Buffer,
+ UserParms->Buffer,
+ length );
+
+ } else {
+
+ newUserParms.Buffer = UserParms->Buffer;
+ newUserParms.Length = (USHORT) length;
+ newUserParms.MaximumLength = UserParms->MaximumLength;
+ }
+
+ //
+ // Slap in null terminator
+ //
+
+ ptr = newUserParms.Buffer + ( length / sizeof(WCHAR) );
+ *ptr = L'\0';
+
+ status = QueryUserProperty( newUserParms.Buffer,
+ PropertyName,
+ PropertyFlag,
+ PropertyValue );
+
+ if (newUserParms.Buffer != UserParms->Buffer) {
+
+ LocalFree( newUserParms.Buffer );
+ }
+
+ return(status);
+}
+
+// Common routine used by QueryUserProperty() and SetUserProperty().
+
+BOOL
+FindUserProperty (
+ PUSER_PROPERTIES UserProperties,
+ LPWSTR Property,
+ PUSER_PROPERTY *pUserProperty,
+ USHORT *pCount
+ )
+{
+ BOOL fFound = FALSE;
+ USHORT PropertyLength;
+
+ //
+ // Check if user has valid property structure attached,
+ // pointed to by UserProperties.
+ //
+
+ if ( ( UserProperties != NULL )
+ && ( lstrlenW( (LPWSTR) UserProperties) * sizeof(WCHAR) >
+ sizeof(UserProperties->Header.BacklevelParms))
+ && ( UserProperties->Header.PropertySignature == USER_PROPERTY_SIGNATURE)
+ )
+ {
+ //
+ // user has valid property structure.
+ //
+
+ *pUserProperty = &(UserProperties->FirstProperty);
+
+ PropertyLength = lstrlenW( Property ) * sizeof(WCHAR);
+
+ for ( *pCount = 1; *pCount <= UserProperties->Header.PropertyCount;
+ (*pCount)++ )
+ {
+ if ( ( PropertyLength == (*pUserProperty)->PropertyLength )
+ && ( RtlCompareMemory( &((*pUserProperty)->Property[0]),
+ Property,
+ PropertyLength ) == PropertyLength )
+ )
+ {
+ fFound = TRUE;
+ break;
+ }
+
+ *pUserProperty = (PUSER_PROPERTY)
+ ((LPSTR) (*pUserProperty)
+ + sizeof( USER_PROPERTY )
+ + (*pUserProperty)->PropertyLength
+ + (*pUserProperty)->ValueLength
+ - sizeof(WCHAR)); // for Property[0]
+ }
+ }
+
+ return( fFound );
+}
+
+
+// Remove a property field from the User Parms.
+
+VOID
+RemoveUserProperty (
+ UNICODE_STRING *puniUserParms,
+ PUSER_PROPERTY UserProperty,
+ USHORT Count,
+ BOOL *Update
+ )
+{
+ PUSER_PROPERTIES UserProperties;
+ PUSER_PROPERTY NextProperty;
+ USHORT OldParmLength;
+
+ UserProperties = (PUSER_PROPERTIES) puniUserParms->Buffer;
+
+ OldParmLength = sizeof( USER_PROPERTY ) +
+ UserProperty->PropertyLength +
+ UserProperty->ValueLength -
+ sizeof(WCHAR); // for Property[0]
+
+
+ NextProperty = (PUSER_PROPERTY)(LPSTR)((LPSTR) UserProperty + OldParmLength);
+
+ //
+ // if we're not on the last one, copy the remaining buffer up
+ //
+
+ if (Count < UserProperties->Header.PropertyCount) {
+
+ RtlMoveMemory( UserProperty,
+ NextProperty,
+ puniUserParms->Length -
+ ((LPSTR) NextProperty -
+ (LPSTR) puniUserParms->Buffer ));
+ }
+
+ //
+ // Now reduce the length of the buffer by the amount we pulled out
+ //
+
+ puniUserParms->Length -= OldParmLength;
+
+ UserProperties->Header.PropertyCount--;
+
+ *Update = TRUE;
+}
+
+
+NTSTATUS
+UserPropertyAllocBlock (
+ IN PUNICODE_STRING Existing,
+ IN ULONG DesiredLength,
+ IN OUT PUNICODE_STRING New
+ )
+/*
+ This allocates a larger block for user's parameters and copies the old
+ block in.
+*/
+{
+ PUSER_PROPERTIES UserProperties;
+ CLONG Count;
+ WCHAR *pNewBuff;
+
+
+ //
+ // We will allocate a new buffer to store the new parameters
+ // and copy the existing parameters into it.
+ //
+
+ New->Buffer = LocalAlloc (LPTR, DesiredLength);
+
+ if ( New->Buffer == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ New->MaximumLength = (USHORT) DesiredLength;
+
+ if (Existing != NULL)
+ {
+
+ New->Length = Existing->Length;
+
+ RtlCopyMemory( New->Buffer,
+ Existing->Buffer,
+ Existing->Length );
+ }
+ else
+ {
+ New->Length = 0;
+ }
+
+ //
+ // Ensure that we don't have any nulls in our string.
+ //
+
+ for ( Count = 0;
+ Count < New->Length / sizeof(WCHAR);
+ Count++ )
+ {
+ if (*(New->Buffer + Count) == L'\0')
+ {
+ New->Length = (USHORT) Count * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ //
+ // now pad it out with spaces until reached Mac+Ras reserved length
+ //
+
+ pNewBuff = (WCHAR *) New->Buffer + ( New->Length / sizeof(WCHAR) );
+
+ while ( New->Length < sizeof(UserProperties->Header.BacklevelParms))
+ {
+ *( pNewBuff++ ) = L' ';
+ New->Length += sizeof(WCHAR);
+ }
+
+ //
+ // If the signature isn't there, stick it in and set prop count to 0
+ //
+
+ UserProperties = (PUSER_PROPERTIES) New->Buffer;
+
+ if (New->Length < sizeof(USER_PROPERTIES_HDR) ||
+ UserProperties->Header.PropertySignature != USER_PROPERTY_SIGNATURE)
+ {
+
+ UserProperties->Header.PropertySignature = USER_PROPERTY_SIGNATURE;
+ UserProperties->Header.PropertyCount = 0;
+
+ New->Length = sizeof(USER_PROPERTIES_HDR);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+// usrprop.c eof.
+
+