diff options
Diffstat (limited to 'private/net/svcdlls/fpnw/client')
-rw-r--r-- | private/net/svcdlls/fpnw/client/encrypt.c | 314 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/fpnwclnt.def | 56 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/fpnwclnt.rc | 10 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/logon.c | 1131 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/makefile | 6 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/makefile.inc | 1 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/ncpbind.c | 117 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/ncpstub.c | 1435 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/notify.c | 543 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/nwsutil.c | 213 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/sources | 69 | ||||
-rw-r--r-- | private/net/svcdlls/fpnw/client/usrprop.c | 831 |
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 (¤tTime); + + uPropertyValue.Buffer = (PWCHAR) ¤tTime; + 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. + + |