summaryrefslogtreecommitdiffstats
path: root/private/nw/svcdlls/nwwks/client/logon.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/svcdlls/nwwks/client/logon.c')
-rw-r--r--private/nw/svcdlls/nwwks/client/logon.c2539
1 files changed, 2539 insertions, 0 deletions
diff --git a/private/nw/svcdlls/nwwks/client/logon.c b/private/nw/svcdlls/nwwks/client/logon.c
new file mode 100644
index 000000000..f539fce2c
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/logon.c
@@ -0,0 +1,2539 @@
+/*++
+
+Copyright (c) 1993, 1994 Microsoft Corporation
+
+Module Name:
+
+ logon.c
+
+Abstract:
+
+ This module contains NetWare credential management code.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+ Yi-Hsin Sung (yihsins) 10-July-1993
+ Moved all dialog handling to nwdlg.c
+
+--*/
+
+#include <nwclient.h>
+#include <ntmsv1_0.h>
+#include <nwsnames.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwevent.h>
+
+#include <nwdlg.h>
+
+#include <nwreg.h>
+#include <nwlsa.h>
+#include <nwauth.h>
+#include <nwapi.h>
+#include <nwmisc.h>
+#include <ndsapi32.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+NwpInitializeRegistry(
+ IN LPWSTR NewUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD LogonScriptOptions,
+ OUT PDWORD PrintOption
+ );
+
+DWORD
+NwpReadRegInfo(
+ IN HKEY WkstaKey,
+ IN LPWSTR CurrentUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD PrintOption
+ );
+
+DWORD
+NwpGetCurrentUser(
+ OUT LPWSTR *SidString,
+ OUT LPWSTR *UserName
+ );
+
+DWORD
+NwpGetUserSid(
+ IN PLUID LogonId,
+ OUT LPWSTR *UserSidString
+ );
+
+BOOL
+NwpPollWorkstationStart(
+ VOID
+ );
+
+VOID
+NwpSaveServiceCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+DWORD
+NwpSetCredentialInLsa(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+NTSTATUS NwNdsOpenRdrHandle(
+ OUT PHANDLE phNwRdrHandle
+ );
+
+DWORD
+NwpReadLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD pPreferredServerExists
+ );
+
+LPWSTR
+NwpConstructLogonScript(
+ IN DWORD LogonScriptOptions
+ );
+
+VOID
+NwpSelectServers(
+ IN HWND DialogHandle,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ );
+
+////////////////////////////////////////////////////////////////////////////
+
+DWORD
+APIENTRY
+NPLogonNotify(
+ PLUID lpLogonId,
+ LPCWSTR lpAuthentInfoType,
+ LPVOID lpAuthentInfo,
+ LPCWSTR lpPreviousAuthentInfoType,
+ LPVOID lpPreviousAuthentInfo,
+ LPWSTR lpStationName,
+ LPVOID StationHandle,
+ LPWSTR *lpLogonScript
+ )
+/*++
+
+Routine Description:
+
+ This function is called by Winlogon after the interactive
+ user has successfully logged on to the local machine. We
+ are given the username and password, which
+ are displayed in the NetWare specific logon dialog if
+ needed.
+
+Arguments:
+
+ lpLogonId - Ignored.
+
+ lpAuthentInfoType - Supplies a string which if is
+ L"MSV1_0:Interactive" means that the user has been logged
+ on by the Microsoft primary authenticator.
+
+ lpAuthentInfo - Supplies a pointer to the credentials which
+ the user was logged on with.
+
+ lpPreviousAuthentInfoType - Ignored.
+
+ lpPreviousAuthentInfo - Ignored.
+
+ lpStationName - Supplies a string which if it is L"WinSta_0"
+ means that Winlogon logged on the user.
+
+ StationHandle - Supplies the handle to the window which to display
+ our specific dialog.
+
+ lpLogonScripts - Receives a pointer to memory allocated by this
+ routine which contains a MULTI_SZ string of a program to run on
+ the command line with arguments, e.g. L"myprogram\0arg1\0arg2\0".
+ This memory must be freed by the caller with LocalFree.
+
+Return Value:
+
+ WN_SUCCESS - Successfully saved default credentials.
+
+ WN_NOT_SUPPORTED - Primary authenticator is not Microsoft or
+ is not interactive via Winlogon.
+
+ ERROR_FILE_NOT_FOUND - Could not get our own provider DLL handle.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ int Result = FALSE;
+ LPWSTR NewUserSid = NULL;
+ BOOL LogonAttempted = FALSE;
+ PMSV1_0_INTERACTIVE_LOGON NewLogonInfo =
+ (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;
+
+ WCHAR NwpServerBuffer[MAX_PATH + 1];
+ WCHAR NwpPasswordBuffer[NW_MAX_PASSWORD_LEN + 1];
+ DWORD NwpPrintOption = NW_PRINT_OPTION_DEFAULT;
+ DWORD NwpLogonScriptOptions = NW_LOGONSCRIPT_DEFAULT ;
+ BOOL cPasswordDlgClickOK = 0;
+ BOOL ServiceLogin = FALSE ;
+
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfoType);
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfo);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\nNWPROVAU: NPLogonNotify\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (_wcsicmp(lpAuthentInfoType, L"MSV1_0:Interactive") != 0)
+ {
+
+ //
+ // We only handle a logon where Microsoft is the primary
+ // authenticator and it is an interactive logon via Winlogon.
+ //
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+ if (_wcsicmp(lpStationName, L"SvcCtl") == 0)
+ {
+ ServiceLogin = TRUE ;
+ }
+
+
+ //
+ // Initialize credential variables
+ //
+ NwpServerBuffer[0] = NW_INVALID_SERVER_CHAR;
+ NwpServerBuffer[1] = 0;
+
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+
+ if (NewLogonInfo->Password.Buffer != NULL) {
+
+ //
+ // check for max length to avoid overflowing.
+ //
+ if (NewLogonInfo->Password.Length >
+ (sizeof(NwpPasswordBuffer) - sizeof(WCHAR))) {
+
+ status = ERROR_INVALID_PARAMETER ;
+ goto EndOfTry;
+ }
+
+ wcsncpy(
+ NwpPasswordBuffer,
+ NewLogonInfo->Password.Buffer,
+ NewLogonInfo->Password.Length / sizeof(WCHAR)
+ );
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\tMessageType : %lu\n", NewLogonInfo->MessageType));
+ KdPrint(("\tLogonDomainName : %ws\n", NewLogonInfo->LogonDomainName.Buffer));
+ KdPrint(("\tUserName : %ws\n", NewLogonInfo->UserName.Buffer));
+ KdPrint(("\tPassword : %ws\n", NwpPasswordBuffer));
+ }
+#endif
+
+ //
+ // if Interactive login, get user related info
+ //
+ if (!ServiceLogin)
+ {
+ //
+ // Get the user SID so that the user Netware username and
+ // preferred server is saved under a SID key rather than the
+ // LogonDomain*UserName key. We do this by making ourselves
+ // a logon process, and call the special MSV1.0 GetUserInfo
+ // interface.
+ //
+ status = NwpGetUserSid(lpLogonId, &NewUserSid);
+
+ if (status != NO_ERROR) {
+ goto EndOfTry;
+ }
+
+ //
+ // Initialize the registry:
+ // 1) Delete the CurrentUser value if it exists (was not clean up
+ // previously because user did not log off--rebooted machine).
+ // 2) Read the current user's PreferredServer and PrintOption
+ // value so that we can display the user's original
+ // preferred server.
+ //
+ NwpInitializeRegistry( NewUserSid,
+ NwpServerBuffer,
+ sizeof( NwpServerBuffer ) /
+ sizeof( NwpServerBuffer[0]),
+ &NwpLogonScriptOptions,
+ &NwpPrintOption );
+ }
+
+ //
+ // Poll until the NetWare workstation has started, then validate
+ // the user credential.
+ //
+ (void) NwpPollWorkstationStart();
+
+ //
+ // If service login, notify the redir with the username/passwd/
+ // LUID triplet and save the logon ID in the registry so that
+ // workstation can pick up if stopped and restarted.
+ //
+ if (ServiceLogin)
+ {
+ NwpSaveServiceCredential(
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer
+ );
+
+ (void) NwrLogonUser(
+ NULL,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NULL,
+ NULL,
+ 0
+ );
+
+ }
+ else
+ {
+ //
+ // We need to save the user credentials at least once so that
+ // the CURRENTUSER Value is stored in the registry.
+ // This must be done before any RPC calls but after polling
+ // workstation start.
+ //
+ NwpSaveLogonCredential(
+ NewUserSid,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NULL // Don't save the preferred server
+ );
+
+//
+// Let the logon set the printer options for each user.
+// Set the provider name and burst options in the service also.
+//
+ //
+ // We need to set the print option at least once.
+ // This also makes the redir aware of the provider name,
+ //
+ (void) NwrSetInfo(
+ NULL,
+ NwpPrintOption,
+ NULL // Need to be NULL so that we will not try to
+ // attach to the server before logging on
+ );
+
+
+ if (*NwpServerBuffer != NW_INVALID_SERVER_CHAR ) {
+
+ //
+ // Preferred server exists. So, try to log the user on.
+ //
+ INT nResult;
+
+ while (1)
+ {
+ WCHAR *DefaultTree = NULL ;
+ WCHAR *DefaultContext = NULL;
+ WCHAR *PreferredServer = NULL;
+ PROMPTDLGPARAM PasswdPromptParam;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\tNwrLogonUser\n"));
+ KdPrint(("\tUserName : %ws\n",NewLogonInfo->UserName.Buffer));
+ KdPrint(("\tServer : %ws\n", NwpServerBuffer));
+ }
+#endif
+
+
+ //
+ // make sure user is logged off
+ //
+ (void) NwrLogoffUser(NULL, lpLogonId) ;
+
+ status = NwrLogonUser(
+ NULL,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NwpServerBuffer, // now either TREE or SERVER
+ NULL,
+ 0
+ );
+
+
+ if (status != ERROR_INVALID_PASSWORD)
+ break ;
+
+ PasswdPromptParam.UserName = NewLogonInfo->UserName.Buffer;
+ PasswdPromptParam.ServerName = NwpServerBuffer ;
+ PasswdPromptParam.Password = NwpPasswordBuffer;
+ PasswdPromptParam.PasswordSize = sizeof(NwpPasswordBuffer)/
+ sizeof(NwpPasswordBuffer[0]) ;
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PASSWORD_PROMPT),
+ (HWND) StationHandle,
+ NwpPasswdPromptDlgProc,
+ (LPARAM) &PasswdPromptParam
+ );
+
+ if (Result == -1 || Result == IDCANCEL)
+ {
+ status = ERROR_INVALID_PASSWORD ;
+ break ;
+ }
+ else
+ {
+ cPasswordDlgClickOK++;
+ }
+ }
+
+ if (status == NW_PASSWORD_HAS_EXPIRED)
+ {
+ WCHAR szNumber[16] ;
+ DWORD status1, dwMsgId, dwGraceLogins = 0 ;
+ LPWSTR apszInsertStrings[3] ;
+
+ //
+ // get the grace login count
+ //
+ status1 = NwGetGraceLoginCount(
+ NwpServerBuffer, // BUGBUG
+ NewLogonInfo->UserName.Buffer,
+ &dwGraceLogins) ;
+
+ //
+ // if hit error, just dont use the number
+ //
+ if (status1 == NO_ERROR)
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED ; // use setpass.exe
+ wsprintfW(szNumber, L"%ld", dwGraceLogins) ;
+ }
+ else
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED1 ; // use setpass.exe
+ }
+
+ apszInsertStrings[0] = NwpServerBuffer ; // BUGBUG
+ apszInsertStrings[1] = szNumber ;
+ apszInsertStrings[2] = NULL ;
+
+ //
+ // put up message on password expiry
+ //
+ (void) NwpMessageBoxIns(
+ (HWND) StationHandle,
+ IDS_NETWARE_TITLE,
+ dwMsgId,
+ apszInsertStrings,
+ MB_OK | MB_SETFOREGROUND |
+ MB_ICONINFORMATION );
+
+ status = NO_ERROR ;
+ }
+
+
+ if ( status != NO_ERROR )
+ {
+ WCHAR *pszErrorLocation = NwpServerBuffer ; //BUGBUG
+ DWORD dwMsgId = IDS_LOGIN_FAILURE_WARNING;
+
+ if (status == ERROR_ACCOUNT_RESTRICTION)
+ {
+ dwMsgId = IDS_LOGIN_ACC_RESTRICTION;
+ }
+
+ if (status == ERROR_SHARING_PAUSED)
+ {
+ status = IDS_LOGIN_DISABLED;
+ }
+
+ if (*NwpServerBuffer == L'*')
+ {
+ //
+ // Format into nicer string for user
+ //
+ WCHAR *pszTmp = LocalAlloc(LMEM_ZEROINIT,
+ (wcslen(NwpServerBuffer)+2) *
+ sizeof(WCHAR)) ;
+ if (pszTmp)
+ {
+ pszErrorLocation = pszTmp ;
+
+ //
+ // This code formats the NDS
+ // tree UNC to: Tree(Context)
+ //
+ wcscpy(pszErrorLocation, NwpServerBuffer+1) ;
+
+ if (pszTmp = wcschr(pszErrorLocation, L'\\'))
+ {
+ *pszTmp = L'(' ;
+ wcscat(pszErrorLocation, L")") ;
+ }
+ }
+ }
+
+ nResult = NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ dwMsgId,
+ status,
+ pszErrorLocation,
+ MB_YESNO | MB_ICONEXCLAMATION );
+
+ if (pszErrorLocation != NwpServerBuffer)
+ {
+ (void) LocalFree(pszErrorLocation) ;
+ }
+
+ //
+ // User chose not to select another preferred server,
+ // hence just return success.
+ //
+ if ( nResult == IDNO ) {
+ status = NO_ERROR;
+ }
+ }
+
+ //
+ // The user might have changed the password in the password
+ // prompt dialog. Hence, we need to save the credentials
+ // ( the password ) again. Although the user might choose
+ // to select another server, he might canceled out of the
+ // login dialog. We must save logon credentials here no matter
+ // what.
+ //
+ NwpSaveLogonCredential(
+ NewUserSid,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NwpServerBuffer
+ );
+ }
+
+ //
+ // Only prompt user with the NetWare login dialog if
+ // no preferred server was found or an error occurred
+ // while authenticating the user.
+ //
+ if ( ( status != NO_ERROR)
+ || (*NwpServerBuffer == NW_INVALID_SERVER_CHAR)
+ )
+ {
+
+ LOGINDLGPARAM LoginParam;
+
+ if ( cPasswordDlgClickOK > 0 )
+ {
+ // Password might have changed in the password prompt
+ // dialog. We want to always first use the NT password
+ // when validating a user on a server. Hence,
+ // we need to copy back the original NT password into
+ // NwpPasswordBuffer.
+
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+ if (NewLogonInfo->Password.Buffer != NULL)
+ {
+ wcsncpy(
+ NwpPasswordBuffer,
+ NewLogonInfo->Password.Buffer,
+ NewLogonInfo->Password.Length / sizeof(WCHAR)
+ );
+ }
+ }
+
+ LoginParam.UserName = NewLogonInfo->UserName.Buffer;
+ LoginParam.ServerName = NwpServerBuffer ;
+ LoginParam.Password = NwpPasswordBuffer;
+ LoginParam.NewUserSid = NewUserSid;
+ LoginParam.pLogonId = lpLogonId;
+ LoginParam.ServerNameSize = sizeof( NwpServerBuffer ) /
+ sizeof( NwpServerBuffer[0]);
+ LoginParam.PasswordSize = sizeof( NwpPasswordBuffer ) /
+ sizeof( NwpPasswordBuffer[0]);
+ LoginParam.LogonScriptOptions = NwpLogonScriptOptions;
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_NETWARE_LOGIN),
+ (HWND) StationHandle,
+ NwpLoginDlgProc,
+ (LPARAM) &LoginParam
+ );
+
+ if (Result == -1) {
+ status = GetLastError();
+ KdPrint(("NWPROVAU: DialogBox failed %lu\n", status));
+ goto EndOfTry;
+ }
+
+ }
+ }
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+
+#if DBG
+ DWORD XceptCode;
+
+
+ XceptCode = RpcExceptionCode();
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NPLogonNotify: Exception code is x%08lx\n", XceptCode));
+ }
+ status = NwpMapRpcError(XceptCode);
+#else
+ status = NwpMapRpcError(RpcExceptionCode());
+#endif
+
+ }
+ RpcEndExcept;
+
+
+ if (!ServiceLogin) {
+ DWORD fPServer = 0;
+
+ NwpReadLogonScriptOptions( NewUserSid,
+ &NwpLogonScriptOptions,
+ &fPServer );
+ if ( fPServer && ( NwpLogonScriptOptions & NW_LOGONSCRIPT_ENABLED ) )
+ {
+ *lpLogonScript = NwpConstructLogonScript( NwpLogonScriptOptions );
+
+ //
+ // set scripts to run synchronously. ignore error if we cant.
+ // not a disaster.
+ //
+ (void) NwrSetLogonScript(NULL, SYNC_LOGONSCRIPT) ;
+ }
+ else
+ {
+ *lpLogonScript = LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR));
+ }
+ }
+ else
+ *lpLogonScript = NULL;
+
+ if (NewUserSid != NULL) {
+ (void) LocalFree((HLOCAL) NewUserSid);
+ }
+
+ //
+ // Clear the password
+ //
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+
+ if (status == WN_NO_NETWORK) {
+ //
+ // We don't care if the workstation has not started because
+ // we tuck the logon credential in the registry to be picked
+ // up by the workstation when it starts up. If we return
+ // ERROR_NO_NETWORK, MPR will poll us forever, causing us
+ // to continuously display the login dialog over and over
+ // again.
+ //
+ status = NO_ERROR;
+ }
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPPasswordChangeNotify(
+ LPCWSTR lpAuthentInfoType,
+ LPVOID lpAuthentInfo,
+ LPCWSTR lpPreviousAuthentInfoType,
+ LPVOID lpPreviousAuthentInfo,
+ LPWSTR lpStationName,
+ LPVOID StationHandle,
+ DWORD dwChangeInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is called after the interactive user has selected to
+ change the password for the local logon via the Ctrl-Alt-Del dialog.
+ It is also called when the user cannot login because the password
+ has expired and must be changed.
+
+Arguments:
+
+ lpAuthentInfoType - Supplies a string which if is
+ L"MSV1_0:Interactive" means that the user has been logged
+ on by the Microsoft primary authenticator.
+
+ lpAuthentInfo - Supplies a pointer to the credentials to
+ change to.
+
+ lpPreviousAuthentInfoType - Supplies a pointer to the old
+ credentials.
+
+ lpPreviousAuthentInfo - Ignored.
+
+ lpStationName - Supplies a string which if it is L"WinSta_0"
+ means that Winlogon logged on the user.
+
+ StationHandle - Supplies the handle to the window which to display
+ our specific dialog.
+
+ dwChangeInfo - Ignored.
+
+Return Value:
+
+ WN_SUCCESS - successful operation.
+
+ WN_NOT_SUPPORTED - Only support change password if MS v1.0 is
+ the primary authenticator and is done through Winlogon.
+
+ WN_NO_NETWORK - Workstation service did not start.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+
+ CHANGE_PW_DLG_PARAM Credential;
+
+ PMSV1_0_INTERACTIVE_LOGON NewCredential =
+ (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;
+ PMSV1_0_INTERACTIVE_LOGON OldCredential =
+ (PMSV1_0_INTERACTIVE_LOGON) lpPreviousAuthentInfo;
+
+
+
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfoType);
+ DBG_UNREFERENCED_PARAMETER(dwChangeInfo);
+
+ Credential.UserName = NULL;
+
+ RpcTryExcept {
+
+ if ((_wcsicmp(lpAuthentInfoType, L"MSV1_0:Interactive") != 0) ||
+ (_wcsicmp(lpStationName, L"WinSta0") != 0)) {
+
+ //
+ // We only handle a logon where Microsoft is the primary
+ // authenticator and it is an interactive logon via Winlogon.
+ //
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+
+ if (NewCredential == NULL || OldCredential == NULL) {
+
+ //
+ // Credentials not given to us by Winlogon or
+ // user did not type the old and new passwords.
+ //
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: PasswordChangeNotify got NULL for new and old credential pointers\n"));
+ }
+#endif
+
+ (void) NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ IDS_BAD_PASSWORDS,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+ Credential.UserName = LocalAlloc(
+ LMEM_ZEROINIT,
+ (NW_MAX_USERNAME_LEN + 3 +
+ (2 * NW_MAX_PASSWORD_LEN)) * sizeof(WCHAR)
+ );
+
+ if (Credential.UserName == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto EndOfTry;
+ }
+
+ Credential.OldPassword = (LPWSTR) ((DWORD) Credential.UserName +
+ (NW_MAX_USERNAME_LEN + 1) * sizeof(WCHAR));
+ Credential.NewPassword = (LPWSTR) ((DWORD) Credential.OldPassword +
+ (NW_MAX_PASSWORD_LEN + 1) * sizeof(WCHAR));
+
+
+ if (NewCredential->UserName.Length == 0) {
+
+ //
+ // UserName is not specified. Try to get interactive user's name.
+ //
+
+ DWORD CharNeeded = NW_MAX_USERNAME_LEN + 1;
+
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: PasswordChangeNotify got empty string for username\n"));
+ }
+#endif
+
+ if (! GetUserNameW(Credential.UserName, &CharNeeded)) {
+
+ //
+ // Could not get interactive user's name. Give up.
+ //
+ (void) NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ 0,
+ ERROR_BAD_USERNAME,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+ }
+ }
+ else {
+ wcsncpy(
+ Credential.UserName,
+ NewCredential->UserName.Buffer,
+ NewCredential->UserName.Length / sizeof(WCHAR)
+ );
+ }
+
+ if (OldCredential->Password.Length > 0)
+ {
+ wcsncpy(
+ Credential.OldPassword,
+ OldCredential->Password.Buffer,
+ OldCredential->Password.Length / sizeof(WCHAR)
+ );
+ }
+ else
+ {
+ Credential.OldPassword[0] = 0;
+ }
+
+ if (NewCredential->Password.Length > 0)
+ {
+ wcsncpy(
+ Credential.NewPassword,
+ NewCredential->Password.Buffer,
+ NewCredential->Password.Length / sizeof(WCHAR)
+ );
+ }
+ else
+ {
+ Credential.NewPassword[0] = 0;
+ }
+
+ //
+ // Encode the passwords.
+ //
+ {
+ UCHAR EncodeSeed = NW_ENCODE_SEED2;
+ UNICODE_STRING PasswordStr;
+
+
+ RtlInitUnicodeString(&PasswordStr, Credential.OldPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ RtlInitUnicodeString(&PasswordStr, Credential.NewPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+ }
+
+ NwpSelectServers(StationHandle, &Credential);
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+
+#if DBG
+ DWORD XceptCode;
+
+
+ XceptCode = RpcExceptionCode();
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NPPasswordChangeNotify: Exception code is x%08lx\n", XceptCode));
+ }
+ status = NwpMapRpcError(XceptCode);
+#else
+ status = NwpMapRpcError(RpcExceptionCode());
+#endif
+
+ }
+ RpcEndExcept;
+
+ if (Credential.UserName != NULL) {
+ LocalFree(Credential.UserName);
+ }
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ return status;
+
+}
+
+
+VOID
+NwpInitializeRegistry(
+ IN LPWSTR NewUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD PrintOption
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the registry before putting up the
+ logon dialog.
+ 1) Deletes the CurrentUser value if it was not cleaned up from
+ the last logoff.
+ 2) Reads the current user's original PreferredServer value
+ 3) Reads the current user's PrintOption value
+
+Arguments:
+
+ NewUserSid - Supplies the newly logged on user's SID in string format
+ which is the key name to find the password and preferred server.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+ HKEY WkstaKey;
+
+
+ NwDeleteCurrentUser();
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpInitializeRegistry open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ //
+ // Get user's preferred server information.
+ //
+ (void) NwpReadRegInfo(WkstaKey,
+ NewUserSid,
+ PreferredServer,
+ PreferredServerSize,
+ PrintOption
+ );
+
+ (void) RegCloseKey(WkstaKey);
+ (void) NwpReadLogonScriptOptions( NewUserSid, pLogonScriptOptions, NULL );
+}
+
+
+DWORD
+NwpReadRegInfo(
+ IN HKEY WkstaKey,
+ IN LPWSTR CurrentUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD PrintOption
+ )
+/*++
+
+Routine Description:
+
+ This routine reads the user's preferred server and print option
+ from the registry.
+
+Arguments:
+
+ WkstaKey - Supplies the handle to the parameters key under the NetWare
+ workstation service key.
+
+ CurrentUserSid - Supplies the SID string of the user whose information
+ to read.
+
+ PreferredServer - Receives the user's preferred server.
+
+ PrintOption - Receives the user's print option.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+
+ HKEY UserKey;
+
+ DWORD ValueType;
+ DWORD BytesNeeded;
+
+ //
+ // Open current user's key to read the original preferred server.
+ //
+ RegError = RegOpenKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &UserKey
+ );
+
+ if (RegError != NO_ERROR) {
+
+ if ( (RegError == ERROR_FILE_NOT_FOUND) ||
+ (RegError == ERROR_PATH_NOT_FOUND) ) {
+
+ //
+ // If key doesnt exist assume first time. Use default
+ // if present.
+ //
+
+ LONG RegError1 ;
+ HKEY WkstaParamKey ;
+ DWORD Disposition, dwScriptOptions,
+ dwScriptOptionsSize = sizeof(dwScriptOptions);
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError1 = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaParamKey
+ );
+
+ if (RegError1 != NO_ERROR) {
+
+ return (DWORD) RegError; // return original error
+ }
+
+ BytesNeeded = PreferredServerSize;
+
+ RegError1 = RegQueryValueExW(
+ WkstaParamKey,
+ NW_DEFAULTSERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+
+ if (RegError1 != NO_ERROR) {
+
+ (void) RegCloseKey(WkstaParamKey);
+ PreferredServer[0] = NW_INVALID_SERVER_CHAR;
+ PreferredServer[1] = 0;
+ return (DWORD) RegError; // return original error
+ }
+
+ RegError1 = RegQueryValueExW(
+ WkstaParamKey,
+ NW_DEFAULTSCRIPTOPTIONS_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &dwScriptOptions,
+ &dwScriptOptionsSize
+ );
+
+ (void) RegCloseKey(WkstaParamKey);
+
+ if (RegError1 != NO_ERROR) {
+
+ dwScriptOptions = NW_LOGONSCRIPT_ENABLED |
+ NW_LOGONSCRIPT_4X_ENABLED ;
+ }
+
+ //
+ // We have a default. now write out the info for the current
+ // user now. Note we also write out the login script option.
+ // Errors here are not reported.
+ //
+
+
+ //
+ // Create the key under NWCWorkstation\Parameters\Option\<usersid>
+ //
+ RegError = RegCreateKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | WRITE_DAC,
+ NULL, // security attr
+ &UserKey,
+ &Disposition
+ );
+
+ if (RegError == NO_ERROR) {
+
+ RegError = NwLibSetEverybodyPermission( UserKey,
+ KEY_SET_VALUE );
+
+ if ( RegError == NO_ERROR )
+ {
+ //
+ // Write the PreferredServer. Errors ignored.
+ //
+ RegError = RegSetValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) PreferredServer,
+ (wcslen(PreferredServer) + 1) * sizeof(WCHAR)
+ );
+
+ (void) RegCloseKey(UserKey) ;
+
+ (void) NwpSaveLogonScriptOptions(
+ CurrentUserSid,
+ dwScriptOptions
+ ) ;
+ }
+ else {
+
+ (void) RegCloseKey(UserKey) ;
+ }
+ }
+
+
+ *PrintOption = NW_PRINT_OPTION_DEFAULT;
+ return NO_ERROR;
+
+ }
+ return (DWORD) RegError;
+ }
+
+
+ //
+ // Read PreferredServer value
+ //
+ BytesNeeded = PreferredServerSize;
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+ ASSERT(BytesNeeded <= PreferredServerSize);
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original preferred server failed %lu\n",
+ RegError));
+ }
+#endif
+ PreferredServer[0] = NW_INVALID_SERVER_CHAR; // Display login dialog
+ PreferredServer[1] = 0;
+ goto CleanExit;
+ }
+
+ //
+ // Read PrintOption value into NwpPrintOption.
+ //
+ BytesNeeded = sizeof(PrintOption);
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_PRINTOPTION_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PrintOption,
+ &BytesNeeded
+ );
+
+ if (RegError != NO_ERROR ) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original print option failed %lu\n", RegError));
+ }
+#endif
+
+ *PrintOption = NW_PRINT_OPTION_DEFAULT;
+ goto CleanExit;
+ }
+
+CleanExit:
+
+ (void) RegCloseKey(UserKey);
+
+ return NO_ERROR;
+}
+
+DWORD
+NwpReadLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD pPreferredServerExists
+
+ )
+/*++
+
+Routine Description:
+
+ This routine reads the user's logon script options from the registry.
+
+Arguments:
+
+ CurrentUserSid - Supplies the SID string of the user whose information
+ to read.
+
+ pLogonScriptOptions - Receives the user's script options
+
+ pPreferredServerExists - Prefered server specified
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+
+ HKEY UserKey;
+
+ DWORD ValueType;
+ DWORD BytesNeeded;
+ HKEY WkstaKey;
+ WCHAR PreferredServer[MAX_PATH + 1];
+
+ //
+ // initialize output values
+ //
+ *pLogonScriptOptions = 0 ;
+ if (pPreferredServerExists)
+ *pPreferredServerExists = 0 ;
+
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpReadLogonScriptOptions open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return (DWORD) RegError;
+ }
+
+ //
+ // Open current user's key
+ //
+ RegError = RegOpenKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &UserKey
+ );
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Open of CurrentUser %ws existing key failed %lu\n",
+ CurrentUserSid, RegError));
+ }
+#endif
+ (void) RegCloseKey(WkstaKey);
+ return (DWORD) RegError;
+ }
+
+
+ //
+ // Read LogonScriptOption value
+ //
+ BytesNeeded = sizeof(*pLogonScriptOptions);
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_LOGONSCRIPT_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) pLogonScriptOptions,
+ &BytesNeeded
+ );
+
+ if (RegError != NO_ERROR ) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original logon script option failed %lu\n", RegError));
+ }
+#endif
+
+ // leave *pLogonScriptOptions as 0
+ }
+
+ if ( pPreferredServerExists != NULL ) {
+
+ //
+ // Read PreferredServer value
+ //
+ BytesNeeded = sizeof( PreferredServer );
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+ ASSERT(BytesNeeded <= sizeof(PreferredServer));
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original preferred server failed %lu\n",
+ RegError));
+ }
+#endif
+ *pPreferredServerExists = FALSE;
+ }
+ else {
+ if ( lstrcmp( PreferredServer, L"" ) )
+ *pPreferredServerExists = TRUE;
+ else
+ *pPreferredServerExists = FALSE;
+ }
+ }
+
+ (void) RegCloseKey(UserKey);
+ (void) RegCloseKey(WkstaKey);
+
+ return NO_ERROR;
+}
+
+LPWSTR
+NwpConstructLogonScript(
+ IN DWORD LogonScriptOptions
+)
+/*++
+
+Routine Description:
+
+ This routine constructs the multi-string for the logon script,
+ based on the options
+
+Arguments:
+
+ LogonScriptOptions - Logon Script options
+
+Return Value:
+
+ Allocated multi-string
+
+--*/
+{
+ LPWSTR pLogonScript;
+ DWORD BytesNeeded;
+
+#define NW_NETWARE_SCRIPT_NAME L"nwscript.exe"
+#define NW_NETWARE_DEBUG_NAME L"ntsd "
+
+ if ( !( LogonScriptOptions & NW_LOGONSCRIPT_ENABLED ) ) {
+ return NULL;
+ }
+
+ BytesNeeded = MAX_PATH * sizeof(WCHAR);
+
+ if (pLogonScript = LocalAlloc( LMEM_ZEROINIT, BytesNeeded))
+ {
+ DWORD dwSkipBytes = 0 ;
+ UINT retval ;
+
+#if DBG
+ //
+ // if have exact match then start under NTSD.
+ //
+ if ( LogonScriptOptions == (NW_LOGONSCRIPT_ENABLED |
+ NW_LOGONSCRIPT_4X_ENABLED |
+ NW_LOGONSCRIPT_DEBUG) ) {
+
+ retval = GetSystemDirectory(pLogonScript,
+ BytesNeeded );
+ if (retval == 0) {
+
+ (void)LocalFree(pLogonScript) ;
+ return(NULL) ;
+ }
+ wcscat( pLogonScript, L"\\" );
+ wcscat( pLogonScript, NW_NETWARE_DEBUG_NAME );
+ dwSkipBytes = (retval * sizeof(WCHAR)) +
+ sizeof(NW_NETWARE_DEBUG_NAME) ;
+ BytesNeeded -= dwSkipBytes ;
+ }
+#endif
+
+ retval = GetSystemDirectory(pLogonScript + (dwSkipBytes/sizeof(WCHAR)),
+ BytesNeeded );
+
+ if (retval == 0) {
+
+ (void)LocalFree(pLogonScript) ;
+ return(NULL) ;
+ }
+
+ wcscat( pLogonScript, L"\\" );
+ wcscat( pLogonScript, NW_NETWARE_SCRIPT_NAME );
+ }
+
+ return (pLogonScript);
+
+}
+
+DWORD
+NwpSaveLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ IN DWORD LogonScriptOptions
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the logon script options in the registry.
+
+Arguments:
+
+ CurrentUserSid - Supplies the user's SID string
+
+ LogonScriptOptions - Logon script options
+
+Return Value:
+
+ Error from registry
+
+--*/
+{
+ LONG RegError;
+ HKEY WkstaOptionKey;
+ HKEY CurrentUserOptionKey;
+
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaOptionKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return RegError;
+ }
+
+ //
+ // Open the <NewUser> key under Option
+ //
+ RegError = RegOpenKeyExW(
+ WkstaOptionKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &CurrentUserOptionKey
+ );
+
+ (void) RegCloseKey(WkstaOptionKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions failed to save options %lu\n", RegError));
+ return RegError;
+ }
+
+ //
+ // Write the options
+ //
+ RegError = RegSetValueExW(
+ CurrentUserOptionKey,
+ NW_LOGONSCRIPT_VALUENAME,
+ 0,
+ REG_DWORD,
+ (LPVOID) &LogonScriptOptions,
+ sizeof(LogonScriptOptions)
+ );
+
+ (void) RegCloseKey(CurrentUserOptionKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions failed to save options %lu\n", RegError));
+ }
+
+ return RegError;
+
+}
+
+
+VOID
+NwpSaveLogonCredential(
+ IN LPWSTR NewUserSid,
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password,
+ IN LPWSTR PreferredServer OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the user logon credential in the registry
+ and LSA's memory. This is normally called when NwrLogonUser is
+ successful.
+
+Arguments:
+
+ NewUserSid - Supplies the newly logged on user's SID string to be
+ set as the CurrentUser value as well as the name of the key for
+ the user's preferred server.
+
+ LogonId - Supplies the user's logon ID. If NULL is specified,
+ just read the existing logon ID from the registry rather
+ than save a new one.
+
+ UserName - Supplies the name of the user.
+
+ Password - Supplies the password which the user wants to use on
+ the NetWare network.
+
+ PreferredServer - Supplies the name of the preferred server.
+
+Return Value:
+
+ Error from redirector if login is rejected.
+
+--*/
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY WkstaKey;
+ HKEY WkstaLogonKey;
+ HKEY WkstaOptionKey;
+ HKEY NewUserLogonKey;
+ HKEY NewUserOptionKey;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential: %ws, %ws, %ws, %ws\n",
+ NewUserSid, UserName, Password, PreferredServer));
+ }
+#endif
+
+ //
+ // Write the logon credential to the registry. NewUserSid should be
+ // written last so that if it can be read by the workstation service,
+ // it's an indication that all other credential information has been
+ // written.
+ //
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Logon
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaLogonKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters\\Logon key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaOptionKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ (void) RegCloseKey( WkstaLogonKey );
+ return;
+ }
+
+ //
+ // Open the <NewUser> key under Logon
+ //
+ RegError = RegOpenKeyExW(
+ WkstaLogonKey,
+ NewUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &NewUserLogonKey
+ );
+
+
+ if (RegError == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Logon
+ //
+ RegError = RegCreateKeyExW(
+ WkstaLogonKey,
+ NewUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &NewUserLogonKey,
+ &Disposition
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential create Logon\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaLogonKey);
+ (void) RegCloseKey(WkstaOptionKey);
+ return;
+ }
+ }
+ else if (RegError != NO_ERROR)
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open Logon\\%ws unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaLogonKey);
+ (void) RegCloseKey(WkstaOptionKey);
+ return;
+ }
+
+ (void) RegCloseKey(WkstaLogonKey);
+
+ //
+ // Open the <NewUser> key under Option
+ //
+ RegError = RegOpenKeyExW(
+ WkstaOptionKey,
+ NewUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &NewUserOptionKey
+ );
+
+
+ if (RegError == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Option
+ //
+ RegError = RegCreateKeyExW(
+ WkstaOptionKey,
+ NewUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | WRITE_DAC,
+ NULL, // security attr
+ &NewUserOptionKey,
+ &Disposition
+ );
+
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential create Option\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ RegError = NwLibSetEverybodyPermission( NewUserOptionKey,
+ KEY_SET_VALUE );
+
+ if ( RegError != NO_ERROR )
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential set security on Option\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ }
+ else if (RegError != NO_ERROR)
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open Option\\%ws unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ (void) RegCloseKey(WkstaOptionKey);
+
+ //
+ // Successfully opened or created an existing user entry.
+ // We will now save the credential in LSA.
+ //
+ status = NwpSetCredentialInLsa(
+ LogonId,
+ UserName,
+ Password
+ );
+
+ if (status != NO_ERROR) {
+ //
+ // Could not save new credential.
+ //
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to set credential %lu\n", status));
+ }
+
+ //
+ // Write the logon ID to the registry.
+ //
+ RegError = RegSetValueExW(
+ NewUserLogonKey,
+ NW_LOGONID_VALUENAME,
+ 0,
+ REG_BINARY,
+ (LPVOID) LogonId,
+ sizeof(LUID)
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save logon ID %lu\n", RegError));
+ }
+
+ (void) RegCloseKey(NewUserLogonKey);
+
+ //
+ // If PreferredServer is not supplied, then that means we don't want to
+ // save the preferred server into the registry.
+ //
+
+ if (ARGUMENT_PRESENT(PreferredServer))
+ {
+ //
+ // Write the PreferredServer
+ //
+ RegError = RegSetValueExW(
+ NewUserOptionKey,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) PreferredServer,
+ (wcslen(PreferredServer) + 1) * sizeof(WCHAR)
+ );
+
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save PreferredServer %ws %lu\n", PreferredServer, RegError));
+ }
+ }
+
+ (void) RegCloseKey(NewUserOptionKey);
+
+ //
+ // Write the NewUser string as the CurrentUser value.
+ //
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ RegError = RegSetValueExW(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) NewUserSid,
+ (wcslen(NewUserSid) + 1) * sizeof(WCHAR)
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save NewUser %ws %lu\n", NewUserSid, RegError));
+ }
+
+ RegCloseKey( WkstaKey );
+
+}
+
+
+VOID
+NwpSaveServiceCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the service logon ID in the registry and
+ the credential in LSA's memory.
+
+Arguments:
+
+ LogonId - Supplies the service's logon ID.
+
+ UserName - Supplies the name of the service.
+
+ Password - Supplies the password of the service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY ServiceLogonKey;
+ HKEY LogonIdKey;
+
+ DWORD Disposition;
+ WCHAR LogonIdKeyName[NW_MAX_LOGON_ID_LEN];
+
+ //
+ // Write the logon ID to the registry.
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\ServiceLogon, create if does not exist
+ //
+ RegError = RegCreateKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_SERVICE_LOGON_REGKEY,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &ServiceLogonKey,
+ &Disposition
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential open NWCWorkstation\\Parameters\\ServiceLogon key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ NwLuidToWStr(LogonId, LogonIdKeyName);
+
+ //
+ // Create the logon ID key under ServiceLogon
+ //
+ RegError = RegCreateKeyExW(
+ ServiceLogonKey,
+ LogonIdKeyName,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &LogonIdKey,
+ &Disposition
+ );
+
+ RegCloseKey(ServiceLogonKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential create NWCWorkstation\\Parameters\\ServiceLogon\\<LogonId> key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ RegCloseKey(LogonIdKey);
+
+ //
+ // Save the service logon credential in LSA.
+ //
+ status = NwpSetCredentialInLsa(
+ LogonId,
+ UserName,
+ Password
+ );
+
+ if (status != NO_ERROR) {
+ //
+ // Could not save new credential.
+ //
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential failed to set credential %lu\n", status));
+ }
+}
+
+
+DWORD
+NwpGetUserSid(
+ IN PLUID LogonId,
+ OUT LPWSTR *UserSidString
+ )
+/*++
+
+Routine Description:
+
+ This routine looks up the SID of a user given the user's logon ID.
+ It does this by making the current process a logon process and then
+ call to LSA to get the user SID.
+
+Arguments:
+
+ LogonId - Supplies the logon ID of the user to lookup the SID.
+
+ UserSidString - Receives a pointer to a buffer allocated by this routine
+ which contains the user SID in string form. This must be freed with
+ LocalFree when done.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+ HANDLE LsaHandle;
+ ULONG AuthPackageId;
+
+ MSV1_0_GETUSERINFO_REQUEST UserInfoRequest;
+ PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse = NULL;
+ ULONG UserInfoResponseLength;
+
+
+
+
+ //
+ // Register this process as a logon process so that we can call
+ // MS V 1.0 authentication package.
+ //
+ RtlInitString(&InputString, "Microsoft NetWare Credential Manager");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ &LsaHandle,
+ &SecurityMode
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaRegisterLogonProcess returns x%08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Look up the MS V1.0 authentication package
+ //
+ RtlInitString(&InputString, MSV1_0_PACKAGE_NAME);
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &InputString,
+ &AuthPackageId
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaLookupAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Ask authentication package for user information.
+ //
+ UserInfoRequest.MessageType = MsV1_0GetUserInfo;
+ RtlCopyLuid(&UserInfoRequest.LogonId, LogonId);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &UserInfoRequest,
+ sizeof(MSV1_0_GETUSERINFO_REQUEST),
+ (PVOID *) &UserInfoResponse,
+ &UserInfoResponseLength,
+ &AuthPackageStatus
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = AuthPackageStatus;
+ }
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaCallAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Convert the SID to string. This routine also allocates the
+ // output buffer.
+ //
+ status = NwpConvertSid(
+ UserInfoResponse->UserSid,
+ UserSidString
+ );
+
+CleanExit:
+ if (UserInfoResponse != NULL) {
+ (void) LsaFreeReturnBuffer((PVOID) UserInfoResponse);
+ }
+
+ (void) LsaDeregisterLogonProcess(LsaHandle);
+
+ return status;
+}
+
+
+DWORD
+NwpConvertSid(
+ IN PSID Sid,
+ OUT LPWSTR *UserSidString
+ )
+{
+ NTSTATUS ntstatus;
+ UNICODE_STRING SidString;
+
+
+ //
+ // Initialize output pointer
+ //
+ *UserSidString = NULL;
+
+ ntstatus = RtlConvertSidToUnicodeString(
+ &SidString,
+ Sid,
+ TRUE // Allocate destination string
+ );
+
+ if (ntstatus != STATUS_SUCCESS) {
+ KdPrint(("NWPROVAU: RtlConvertSidToUnicodeString returns %08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Create the buffer to return the SID string
+ //
+ if ((*UserSidString = (LPVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ SidString.Length + sizeof(WCHAR)
+ )) == NULL) {
+ RtlFreeUnicodeString(&SidString);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ memcpy(*UserSidString, SidString.Buffer, SidString.Length);
+
+ RtlFreeUnicodeString(&SidString);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpConvertSid got %ws\n", *UserSidString));
+ }
+#endif
+
+ return NO_ERROR;
+}
+
+
+BOOL
+NwpPollWorkstationStart(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine polls for the workstation to complete starting.
+ It gives up after 90 seconds.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns TRUE if the NetWare workstation is running; FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+ SC_HANDLE ScManager = NULL;
+ SC_HANDLE Service = NULL;
+ SERVICE_STATUS ServiceStatus;
+ DWORD TryCount = 0;
+ BOOL Started = FALSE;
+
+
+ if ((ScManager = OpenSCManager(
+ NULL,
+ NULL,
+ SC_MANAGER_CONNECT
+ )) == (SC_HANDLE) NULL) {
+
+ err = GetLastError();
+
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: OpenSCManager failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+ if ((Service = OpenService(
+ ScManager,
+ NW_WORKSTATION_SERVICE,
+ SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG
+ )) == (SC_HANDLE) NULL) {
+
+ err = GetLastError();
+
+ (void) CloseServiceHandle(ScManager);
+
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: OpenService failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+
+ do {
+ if (! QueryServiceStatus(
+ Service,
+ &ServiceStatus
+ )) {
+
+ err = GetLastError();
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: QueryServiceStatus failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+ if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
+
+ Started = TRUE;
+ }
+ else if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING ||
+ (ServiceStatus.dwCurrentState == SERVICE_STOPPED &&
+ ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED)) {
+
+ //
+ // If workstation is stopped and never started before but it's
+ // not auto-start, don't poll.
+ //
+ if (TryCount == 0 &&
+ ServiceStatus.dwCurrentState == SERVICE_STOPPED &&
+ ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED) {
+
+ BYTE OutBuffer[sizeof(QUERY_SERVICE_CONFIGW) + 256];
+ DWORD BytesNeeded;
+
+
+ if (QueryServiceConfigW(
+ Service,
+ (LPQUERY_SERVICE_CONFIGW) OutBuffer,
+ sizeof(OutBuffer),
+ &BytesNeeded
+ )) {
+
+ if (((LPQUERY_SERVICE_CONFIGW) OutBuffer)->dwStartType !=
+ SERVICE_AUTO_START) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: Not waiting for the workstation to start\n"));
+ }
+#endif
+
+ goto CleanExit;
+ }
+ }
+ else {
+ err = GetLastError();
+ KdPrint(("NWPROVAU: QueryServiceConfig failed %lu, BytesNeeded %lu\n",
+ err, BytesNeeded));
+ }
+
+ }
+
+
+ //
+ // Wait only if the workstation is start pending, or it has not
+ // been attempted to start before.
+ //
+
+ Sleep(5000); // Sleep for 5 seconds before rechecking.
+ TryCount++;
+ }
+ else {
+ goto CleanExit;
+ }
+
+ } while (! Started && TryCount < 18);
+
+ if (Started) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NetWare workstation is started after we've polled %lu times\n",
+ TryCount));
+ }
+#endif
+
+ }
+
+CleanExit:
+ if (ScManager != NULL) {
+ (void) CloseServiceHandle(ScManager);
+ }
+
+ if (Service != NULL) {
+ (void) CloseServiceHandle(Service);
+ }
+
+ return Started;
+}
+
+
+
+DWORD
+NwpSetCredentialInLsa(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine calls to the NetWare authentication package to save
+ the user credential.
+
+Arguments:
+
+ LogonId - Supplies the logon ID of the user.
+
+ UserName - Supplies the username.
+
+ Password - Supplies the password.
+
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+ HANDLE LsaHandle;
+
+ ULONG AuthPackageId;
+
+ NWAUTH_SET_CREDENTIAL_REQUEST SetCredRequest;
+ PCHAR DummyOutput;
+ ULONG DummyOutputLength;
+
+ UNICODE_STRING PasswordStr;
+ UCHAR EncodeSeed = NW_ENCODE_SEED;
+
+
+ //
+ // Register this process as a logon process so that we can call
+ // NetWare authentication package.
+ //
+ RtlInitString(&InputString, "Microsoft NetWare Credential Manager");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ &LsaHandle,
+ &SecurityMode
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaRegisterLogonProcess returns x%08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Look up the NetWare authentication package
+ //
+ RtlInitString(&InputString, NW_AUTH_PACKAGE_NAME);
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &InputString,
+ &AuthPackageId
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaLookupAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Ask authentication package for user information.
+ //
+ SetCredRequest.MessageType = NwAuth_SetCredential;
+ RtlCopyLuid(&SetCredRequest.LogonId, LogonId);
+ wcscpy(SetCredRequest.UserName, UserName);
+ wcscpy(SetCredRequest.Password, Password);
+
+ //
+ // Encode the password.
+ //
+ RtlInitUnicodeString(&PasswordStr, SetCredRequest.Password);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &SetCredRequest,
+ sizeof(SetCredRequest),
+ (PVOID *) &DummyOutput,
+ &DummyOutputLength,
+ &AuthPackageStatus
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = AuthPackageStatus;
+ }
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaCallAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ }
+ else {
+ status = NO_ERROR;
+ }
+
+CleanExit:
+ (void) LsaDeregisterLogonProcess(LsaHandle);
+
+ return status;
+}
+
+NTSTATUS NwNdsOpenRdrHandle(
+ OUT PHANDLE phNwRdrHandle
+)
+{
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | GENERIC_READ;
+
+ WCHAR NameStr[] = L"\\Device\\NwRdr";
+ UNICODE_STRING uOpenName;
+
+ //
+ // Prepare the open name.
+ //
+
+ RtlInitUnicodeString( &uOpenName, NameStr );
+
+ //
+ // Set up the object attributes.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &uOpenName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile(
+ phNwRdrHandle,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_ERROR(ntstatus) &&
+ !NT_INFORMATION(ntstatus) &&
+ !NT_WARNING(ntstatus)) {
+
+ return IoStatusBlock.Status;
+
+ }
+
+ return ntstatus;
+}
+
+VOID
+NwpSelectServers(
+ IN HWND DialogHandle,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ )
+/*++
+
+Routine Description:
+
+ This routine displays the dialog for user to select individual trees
+ to change password on. It then changes the password on the selected
+ list. After the password has been changed, it displays a dialog which lists
+ the 3.X bindery servers where the change could not be made.
+
+Arguments:
+
+ DialogHandle - Supplies the handle to display dialog.
+
+ Credential - Provides on input the old and new passwords, and
+ the logged in user's name. Other field are ignored
+ on input and consecuently used within this function.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ INT Result;
+
+ Credential->TreeList = NULL;
+ Credential->UserList = NULL;
+ Credential->Entries = 0;
+ Credential->ChangedOne = FALSE;
+
+ Result = DialogBoxParamW( hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_SELECT_SERVERS),
+ (HWND) DialogHandle,
+ NwpSelectServersDlgProc,
+ (LPARAM) Credential );
+
+ if ( Result == IDOK )
+ {
+ //
+ // Display list of trees (if any) for which password was changed.
+ //
+ DialogBoxParamW( hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_CHANGED),
+ (HWND) DialogHandle,
+ NwpChangePasswordSuccessDlgProc,
+ (LPARAM) Credential );
+
+ if ( Credential->TreeList != NULL )
+ {
+ LocalFree( Credential->TreeList );
+ }
+
+ //
+ // Display a dialog to tell users to use SetPass if they have an
+ // account on a NetWare 3.X server.
+ //
+ NwpMessageBoxError( DialogHandle,
+ IDS_NETWARE_TITLE,
+ IDS_CHANGE_PASSWORD_INFO,
+ 0,
+ NULL,
+ MB_OK );
+ }
+}