/*++ Copyright (c) 2001 Microsoft Corporation Module Name: ItgRasXp File Name: ItgRasXp.cpp Abstract: This is the entry point file for the ITG RAS Smartcard Support Applet. This app prompts the user for his domain\username and password credentials, and uses these to create a *Session credential that is used for NTLM authentication for servers on the network which do not use Kerberos. Author: Environment: Win32, C++ Revision History: none Notes: --*/ #include #include #include #include #include #include #include #include #include #include #include #define SECURITY_WIN32 #include #include "testaudit.h" #include "res.h" BOOL gfSuccess = FALSE; HINSTANCE ghInstance = NULL; #if 0 #define TESTKEY L"Environment" #else #define TESTKEY L"Software\\Microsoft\\Connection Manager\\Worldwide Dial-Up RAS to MS Corp" #endif // see if name valid - check for common mistakes. At the moment, we merely insist that // the user fill in both username and password, and that the username contains the '\' // character, making it more likely that it is an approved domain\usernaem form. BOOL CheckUsername(WCHAR *pszUsername,WCHAR *pszPassword) { ASSERT(pszUsername); ASSERT(pszPassword); if ((0 == wcslen(pszUsername)) || (0 == wcslen(pszPassword))) { CHECKPOINT(2,"Username and password not both filled in."); MessageBox(NULL,L"Both username and password must be specified.",L"Error",MB_ICONHAND); return FALSE; } if (NULL == wcschr(pszUsername,L'\\')) { CHECKPOINT(3,"Username not domain\\username."); MessageBox(NULL,L"The username format must be \"domain\\username\".",L"Error",MB_ICONHAND); return FALSE; } else return TRUE; } // Attempt to use the credentials that the user entered and return FALSE if they do not // appear to be valid on the net. Show the user any errors that arise from trying to validate // his credentials. In the event that validation is impossible owing to a network error or some // other error not the fault of his credentials, save them anyway, though with a warning. #define DEFAULTSERVER L"\\\\products\\public" BOOL IsCredentialOK(WCHAR *pszUsername,WCHAR *pszPassword) { ASSERT(pszUsername); ASSERT(pszPassword); NETRESOURCE stNetResource; WCHAR szServer[MAX_PATH + 1]; // to hold test host string from registry BOOL fKeyFound = FALSE; DWORD dwErr = 0; DWORD dwSize = 0; // for return from open connection DWORD dwResult = 0; // for return from open connection HKEY hKey= NULL; // reg key rread DWORD dwType; // reg key read return DWORD dwDataSize = 0; // reg key read in/out memset(&stNetResource,0,sizeof(NETRESOURCE)); // prepare the servername preset to the default wcsncpy(szServer,DEFAULTSERVER,MAX_PATH); // Look for server in registry HKCU. If found, use it to overwrite the default server if ((!fKeyFound) && (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,TESTKEY,0,KEY_READ,&hKey))) { if ((hKey) && (ERROR_SUCCESS == RegQueryValueEx(hKey,L"RAS Test Host",0,&dwType,(LPBYTE) NULL,&dwDataSize))) { // key value exists and is of size dwDataSize WCHAR *pString = (WCHAR *) LocalAlloc(LMEM_FIXED,dwDataSize); ASSERT(pString); ASSERT(dwType == REG_SZ); if (pString) { if (ERROR_SUCCESS == RegQueryValueEx(hKey,L"RAS Test Host",0,&dwType,(LPBYTE) pString,&dwDataSize)) { CHECKPOINT(9,"Override server found in registry in HKCU"); wcsncpy(szServer,pString,dwDataSize / sizeof(WCHAR)); fKeyFound = TRUE; } LocalFree(pString); } } RegCloseKey(hKey); hKey = NULL; } // Look for server in registry HKLM. if ((!fKeyFound) && (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,TESTKEY,0,KEY_READ,&hKey))) { if ((hKey) && (ERROR_SUCCESS == RegQueryValueEx(hKey,L"RAS Test Host",0,&dwType,(LPBYTE) NULL,&dwDataSize))) { // key value exists and is of size dwDataSize WCHAR *pString = (WCHAR *) LocalAlloc(LMEM_FIXED,dwDataSize); ASSERT(pString); ASSERT(dwType == REG_SZ); if (pString) { if (ERROR_SUCCESS == RegQueryValueEx(hKey,L"RAS Test Host",0,&dwType,(LPBYTE) pString,&dwDataSize)) { CHECKPOINT(8,"Override server found in registry in HKLM"); wcsncpy(szServer,pString,dwDataSize / sizeof(WCHAR)); fKeyFound = TRUE; } LocalFree(pString); } } RegCloseKey(hKey); hKey = NULL; } #if TESTAUDIT if (!fKeyFound) CHECKPOINT(10,"No override server found in registry"); #endif stNetResource.dwType = RESOURCETYPE_DISK; stNetResource.lpLocalName = NULL; stNetResource.lpRemoteName = szServer; stNetResource.lpProvider = NULL; dwErr = WNetUseConnection( NULL, &stNetResource, pszPassword, pszUsername, 0, NULL, // lpAccessName &dwSize, // size of lpAccessName buffer &dwResult); if (dwErr == S_OK) { // On successful connection, tear down the connection and return success WNetCancelConnection2(szServer, 0, TRUE); return TRUE; } // Errors are handled by presenting a message box. If the server was found and rejected // the creds, don't save them - give the user a chance to correct. If the server was // unavailable, save the creds anyway, but warn the user that they were unvalidated. switch (dwErr) { case ERROR_ACCESS_DENIED: case ERROR_INVALID_PASSWORD: // announce that the password is no good CHECKPOINT(7,"Reached the server, but the creds were no good"); MessageBox(NULL,L"The entered username and password are not correct",L"Error",MB_ICONHAND); break; case ERROR_NO_NET_OR_BAD_PATH: case ERROR_NO_NETWORK: case ERROR_EXTENDED_ERROR: case ERROR_BAD_NET_NAME: case ERROR_BAD_PROVIDER: default: CHECKPOINT(6,"Not able to validate - server unreachable"); MessageBox(NULL,L"Your username and password will be saved for this session, though they could not be verified. They may be incorrect.",L"Error",MB_ICONHAND); return TRUE; // permit them to be saved anyway // announce that we cannot validate, and let them save it anyway break; } return FALSE; } // Store the user's entered credentials on the keyring as a session persisted *Session cred. // Call IsCredentialOK() before storing. If the credentials don't appear correct, present // a message box describing the error and leave the dialog up. BOOL WriteDomainCreds(WCHAR *pszUsername,WCHAR *pszPassword) { CREDENTIAL cred; if (!CheckUsername(pszUsername,pszPassword)) { return FALSE; } if (!IsCredentialOK(pszUsername,pszPassword)) { return FALSE; } memset(&cred,0,sizeof(CREDENTIAL)); cred.TargetName = CRED_SESSION_WILDCARD_NAME_W; cred.UserName = pszUsername; cred.CredentialBlob = (LPBYTE) pszPassword; cred.CredentialBlobSize = (wcslen(pszPassword) * sizeof(WCHAR)); cred.Type = CRED_TYPE_DOMAIN_PASSWORD; cred.Persist = CRED_PERSIST_SESSION; return CredWrite(&cred,0); } INT_PTR CALLBACK DialogProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { INT_PTR ret; HWND hwndCred = NULL; switch (msg) { case WM_COMMAND: // Button clicks. switch(LOWORD(wparam)) { case IDOK: if (HIWORD(wparam) == BN_CLICKED) { WCHAR szUser[UNLEN + 1]; WCHAR szPass[PWLEN + 1]; szUser[0] = 0; szPass[0] = 0; HWND hCc = GetDlgItem(hwnd,IDC_CRED); ASSERT(hCc); Credential_GetUserName(hCc,szUser,UNLEN); Credential_GetPassword(hCc,szPass,PWLEN); // Get contents of the cred control controls and write the session cred gfSuccess = WriteDomainCreds(szUser,szPass); SecureZeroMemory(szPass,sizeof(szPass)); if (gfSuccess) { EndDialog(hwnd,IDOK); } } break; case IDCANCEL: if (HIWORD(wparam) == BN_CLICKED) { CHECKPOINT(5,"Leave the dialog by cancel."); // Exit doing nothing EndDialog(hwnd,IDCANCEL); } break; default: break; } break; default: break; } //return DefWindowProc(hwnd, msg, wparam, lparam); return FALSE; } int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) { CHECKPOINTINIT; ghInstance = hInstance; //OleInitialize(NULL); INITCOMMONCONTROLSEX stICC; BOOL fICC; stICC.dwSize = sizeof(INITCOMMONCONTROLSEX); stICC.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES; fICC = InitCommonControlsEx(&stICC); // Silent fail if there is no preexisting cert credential. CREDENTIAL *pCred = NULL; BOOL fOK = CredRead(L"*Session",CRED_TYPE_DOMAIN_CERTIFICATE,0,&pCred); CredFree(pCred); if (!fOK) { CHECKPOINT(1,"No preexisting certificate cred for *Session"); CHECKPOINTFINISH; return 1; } // Gen up credui if (!CredUIInitControls()) { return 1; } // show the ui INT_PTR iErr = DialogBoxParam( hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), GetForegroundWindow(), DialogProc, NULL); if (iErr != IDOK && iErr != IDCANCEL) { MessageBox(NULL,L"An error occurred saving credential information.",L"Error",MB_OK); CHECKPOINTFINISH; return 0; } else { CHECKPOINT(4,"Sucessfully saved a cred."); CHECKPOINTFINISH; return 1; } }