/*++ Copyright (C) 1997-2001 Microsoft Corporation Module Name: CSSPI.H Abstract: SSPI wrapper implementation for NTLM/MSN network authentication. History: raymcc 15-Jul-97 Created --*/ #include "precomp.h" #include #include #include // #define trace(x) printf x #define trace(x) static BOOL IsNt(void); //*************************************************************************** // // String helper macros // //*************************************************************************** #define Macro_CloneLPWSTR(x) \ (x ? _wcsdup(x) : 0) #define Macro_CloneLPSTR(x) \ (x ? _strdup(x) : 0) #ifdef _UNICODE #define Macro_CloneLPTSTR(x) (x ? _wcsdup(x) : 0) #else #define Macro_CloneLPTSTR(x) (x ? _strdup(x) : 0) #endif //*************************************************************************** // // BOOL IsNt // // DESCRIPTION: // // Returns true if running windows NT. // // RETURN VALUE: // // see description. // //*************************************************************************** // ok static BOOL IsNt(void) { OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(!GetVersionEx(&os)) return FALSE; // should never happen return os.dwPlatformId == VER_PLATFORM_WIN32_NT; } //*************************************************************************** // // CSSPI Static Data Members // //*************************************************************************** ULONG CSSPI::m_uNumPackages = 0; PSecPkgInfo CSSPI::m_pEnumPkgInfo = 0; PSecurityFunctionTable CSSPI::pVtbl = 0; //*************************************************************************** // // CSSPI::Initialize // // This must be called prior to any other operations, but may be // called multiple times. // // Return value: // TRUE on success, FALSE on failure. // //*************************************************************************** // ok BOOL CSSPI::Initialize() { static HMODULE hLib = 0; // If we have already called this function and everything // is already ok, short-circuit. // ====================================================== if (hLib != 0 && pVtbl != 0) { return TRUE; } if (IsNt()) { hLib = LoadLibrary(__TEXT("SECURITY.DLL")); } else { hLib = LoadLibrary(__TEXT("SECUR32.DLL")); } if (hLib == 0) { trace(("CSSPI::Startup() Library failed to load\n")); return FALSE; } #ifdef _UNICODE INIT_SECURITY_INTERFACE_W pInitFn = (INIT_SECURITY_INTERFACE_W) GetProcAddress(hLib, "InitSecurityInterfaceW"); #else INIT_SECURITY_INTERFACE_A pInitFn = (INIT_SECURITY_INTERFACE_A) GetProcAddress(hLib, "InitSecurityInterfaceA"); #endif if (pInitFn == 0) { trace(("CSSPI::Startup() ERROR : Unable to locate function InitSecurityInterface()\n" )); FreeLibrary(hLib); hLib = 0; return FALSE; } pVtbl = pInitFn(); if (pVtbl == 0) { trace(("CSSPI::Startup() ERROR : Function table not found\n")); FreeLibrary(hLib); hLib = 0; return FALSE; } return TRUE; } //*************************************************************************** // // CSSPI::TranslateError // // Translates an error code to a displayable message. // Treat the return value as read-only (you must, since it is 'const'). // //*************************************************************************** // ok const LPTSTR CSSPI::TranslateError( ULONG uCode ) { LPTSTR p = 0; switch (uCode) { case SEC_E_INSUFFICIENT_MEMORY: p = _T("SEC_E_INSUFFICIENT_MEMORY"); break; case SEC_E_INVALID_HANDLE : p = _T("SEC_E_INVALID_HANDLE"); break; case SEC_E_UNSUPPORTED_FUNCTION : p = _T("SEC_E_UNSUPPORTED_FUNCTION"); break; case SEC_E_TARGET_UNKNOWN : p = _T("SEC_E_TARGET_UNKNOWN"); break; case SEC_E_INTERNAL_ERROR : p = _T("SEC_E_INTERNAL_ERROR"); break; case SEC_E_SECPKG_NOT_FOUND : p = _T("SEC_E_SECPKG_NOT_FOUND"); break; case SEC_E_NOT_OWNER : p = _T("SEC_E_NOT_OWNER"); break; case SEC_E_CANNOT_INSTALL : p = _T("SEC_E_CANNOT_INSTALL"); break; case SEC_E_INVALID_TOKEN : p = _T("SEC_E_INVALID_TOKEN"); break; case SEC_E_CANNOT_PACK : p = _T("SEC_E_CANNOT_PACK"); break; case SEC_E_QOP_NOT_SUPPORTED : p = _T("SEC_E_QOP_NOT_SUPPORTED"); break; case SEC_E_NO_IMPERSONATION : p = _T("SEC_E_NO_IMPERSONATION"); break; case SEC_E_LOGON_DENIED : p = _T("SEC_E_LOGON_DENIED"); break; case SEC_E_UNKNOWN_CREDENTIALS : p = _T("SEC_E_UNKNOWN_CREDENTIALS"); break; case SEC_E_NO_CREDENTIALS : p = _T("SEC_E_NO_CREDENTIALS"); break; case SEC_E_MESSAGE_ALTERED : p = _T("SEC_E_MESSAGE_ALTERED"); break; case SEC_E_OUT_OF_SEQUENCE : p = _T("SEC_E_OUT_OF_SEQUENCE"); break; case SEC_E_NO_AUTHENTICATING_AUTHORITY : p = _T("SEC_E_NO_AUTHENTICATING_AUTHORITY"); break; case SEC_I_CONTINUE_NEEDED : p = _T("SEC_I_CONTINUE_NEEDED"); break; case SEC_I_COMPLETE_NEEDED : p = _T("SEC_I_COMPLETE_NEEDED"); break; case SEC_I_COMPLETE_AND_CONTINUE : p = _T("SEC_I_COMPLETE_AND_CONTINUE"); break; case SEC_I_LOCAL_LOGON : p = _T("SEC_I_LOCAL_LOGON"); break; case SEC_E_BAD_PKGID : p = _T("SEC_E_BAD_PKGID"); break; case SEC_E_CONTEXT_EXPIRED : p = _T("SEC_E_CONTEXT_EXPIRED"); break; case SEC_E_INCOMPLETE_MESSAGE : p = _T("SEC_E_INCOMPLETE_MESSAGE"); break; case SEC_E_INCOMPLETE_CREDENTIALS : p = _T("SEC_E_INCOMPLETE_CREDENTIALS"); break; case SEC_E_BUFFER_TOO_SMALL : p = _T("SEC_E_BUFFER_TOO_SMALL"); break; case SEC_I_INCOMPLETE_CREDENTIALS : p = _T("SEC_I_INCOMPLETE_CREDENTIALS"); break; case SEC_I_RENEGOTIATE : p = _T("SEC_I_RENEGOTIATE"); break; default: p = _T(""); } return (const LPTSTR) p; } //*************************************************************************** // // CSSPI::DisplayContextAttributes // // Display authentication context attribute bits in readable form. // //*************************************************************************** // ok void CSSPI::DisplayContextAttributes( ULONG uAttrib ) { if (uAttrib & ISC_RET_DELEGATE) printf("ISC_RET_DELEGATE\n"); if (uAttrib & ISC_RET_MUTUAL_AUTH) printf("ISC_RET_MUTUAL_AUTH\n"); if (uAttrib & ISC_RET_REPLAY_DETECT) printf("ISC_RET_REPLAY_DETECT\n"); if (uAttrib & ISC_RET_SEQUENCE_DETECT) printf("ISC_RET_SEQUENCE_DETECT\n"); if (uAttrib & ISC_RET_CONFIDENTIALITY) printf("ISC_RET_CONFIDENTIALITY\n"); if (uAttrib & ISC_RET_USE_SESSION_KEY) printf("ISC_RET_USE_SESSION_KEY\n"); if (uAttrib & ISC_RET_USED_COLLECTED_CREDS) printf("ISC_RET_USED_COLLECTED_CREDS\n"); if (uAttrib & ISC_RET_USED_SUPPLIED_CREDS) printf("ISC_RET_USED_SUPPLIED_CREDS\n"); if (uAttrib & ISC_RET_ALLOCATED_MEMORY) printf("ISC_RET_ALLOCATED_MEMORY\n"); if (uAttrib & ISC_RET_USED_DCE_STYLE) printf("ISC_RET_USED_DCE_STYLE\n"); if (uAttrib & ISC_RET_DATAGRAM) printf("ISC_RET_DATAGRAM\n"); if (uAttrib & ISC_RET_CONNECTION) printf("ISC_RET_CONNECTION\n"); if (uAttrib & ISC_RET_INTERMEDIATE_RETURN) printf("ISC_RET_INTERMEDIATE_RETURN\n"); if (uAttrib & ISC_RET_CALL_LEVEL) printf("ISC_RET_CALL_LEVEL\n"); if (uAttrib & ISC_RET_EXTENDED_ERROR) printf("ISC_RET_EXTENDED_ERROR\n"); if (uAttrib & ISC_RET_STREAM) printf("ISC_RET_STREAM\n"); if (uAttrib & ISC_RET_INTEGRITY) printf("ISC_RET_INTEGRITY\n"); } //*************************************************************************** // // CSSPI::DisplayPkgInfo // // Dumps package information to console. // //*************************************************************************** // ok void CSSPI::DisplayPkgInfo(PSecPkgInfo pPkg) { printf("---------------------------------------------\n"); printf("Name = <%s>\n", pPkg->Name); printf("Comment = <%s>\n", pPkg->Comment); printf("Version = 0x%X\n", pPkg->wVersion); printf("Max token = %d bytes\n", pPkg->cbMaxToken); printf("DCE RPC Id = 0x%X\n", pPkg->wRPCID); printf("Capabilities = \n"); if (pPkg->fCapabilities & SECPKG_FLAG_INTEGRITY) printf(" SECPKG_FLAG_INTEGRITY\n"); if (pPkg->fCapabilities & SECPKG_FLAG_PRIVACY) printf(" SECPKG_FLAG_PRIVACY\n"); if (pPkg->fCapabilities & SECPKG_FLAG_TOKEN_ONLY) printf(" SECPKG_FLAG_TOKEN_ONLY\n"); if (pPkg->fCapabilities & SECPKG_FLAG_DATAGRAM) printf(" SECPKG_FLAG_DATAGRAM\n"); if (pPkg->fCapabilities & SECPKG_FLAG_CONNECTION) printf(" SECPKG_FLAG_CONNECTION\n"); if (pPkg->fCapabilities & SECPKG_FLAG_MULTI_REQUIRED) printf(" SECPKG_FLAG_MULTI_REQUIRED\n"); if (pPkg->fCapabilities & SECPKG_FLAG_CLIENT_ONLY) printf(" SECPKG_FLAG_CLIENT_ONLY\n"); if (pPkg->fCapabilities & SECPKG_FLAG_EXTENDED_ERROR) printf(" SECPKG_FLAG_EXTENDED_ERROR\n"); if (pPkg->fCapabilities & SECPKG_FLAG_IMPERSONATION) printf(" SECPKG_FLAG_IMPERSONATION\n"); if (pPkg->fCapabilities & SECPKG_FLAG_ACCEPT_WIN32_NAME) printf(" SECPKG_FLAG_ACCEPT_WIN32_NAME\n"); if (pPkg->fCapabilities & SECPKG_FLAG_STREAM) printf(" SECPKG_FLAG_STREAM\n"); printf("---------------------------------------------\n"); } //*************************************************************************** // // CSSPI::GetNumPkgs // // Gets the number of SSPI packages available on the current machine. // // Returns 0 if none available. // //*************************************************************************** // ok ULONG CSSPI::GetNumPkgs() { // Short-circuit to see if this function has been called before. // It is not possible for new SSPI packages to appear between // reboots, so we cache all info. // ============================================================= if (m_uNumPackages != 0 && m_pEnumPkgInfo) return m_uNumPackages; // Enumerate security packages. // ============================ SECURITY_STATUS SecStatus = pVtbl->EnumerateSecurityPackages(&m_uNumPackages, &m_pEnumPkgInfo); if (SecStatus) { trace(("EnumerateSecurityPackages() failed. Error = %s\n", TranslateError(SecStatus) )); return 0; } return m_uNumPackages; } //*************************************************************************** // // CSSPI::GetPkgInfo // // Retrieves a single read-only PSecPkgInfo pointer, describing the // requested package. This is to be used in conjunction with GetNumPkgs() // in order to iterate through the SSPI package list. // // Returns NULL on error or a read-only PSecPkgInfo pointer. // //*************************************************************************** // ok const PSecPkgInfo CSSPI::GetPkgInfo(ULONG ulPkgNum) { if (ulPkgNum >= m_uNumPackages) return 0; if (m_pEnumPkgInfo == 0) return 0; return &m_pEnumPkgInfo[ulPkgNum]; } //*************************************************************************** // // CSSPI::DumpSecurityPackages // // Dumps all available security packages to the console. // //*************************************************************************** // ok BOOL CSSPI::DumpSecurityPackages() { // Enumerate security packages. // ============================ unsigned long lNum = 0; PSecPkgInfo pPkgInfo; lNum = GetNumPkgs(); if (lNum == 0) return FALSE; trace(("SSPI Supports %d security packages\n", lNum)); for (unsigned long i = 0; i < lNum; i++) { pPkgInfo = GetPkgInfo(i); if (pPkgInfo) DisplayPkgInfo(pPkgInfo); } return TRUE; } //*************************************************************************** // // CSSPI::ServerSupport // // Determines whether the a server-side package can be expected to work. // // Parameters: // The authentication package. Usually "NTLM" // // Return value: // TRUE if the package will support a server-side authentication, FALSE // if not supported. // //*************************************************************************** BOOL CSSPI::ServerSupport(LPTSTR pszPkgName) { if (pszPkgName == 0 || lstrlen(pszPkgName) == 0) return FALSE; if (Initialize() == FALSE) return FALSE; CSSPIServer Server(pszPkgName); if (Server.GetStatus() != CSSPIServer::Waiting) return FALSE; return TRUE; } //*************************************************************************** // // CSSPI::ClientSupport // // Determines whether the a client-side package can be expected to work. // // Parameters: // The authentication package. Usually "NTLM" // // Return value: // TRUE if the package will support a client-side authentication, FALSE // if not supported. // //*************************************************************************** BOOL CSSPI::ClientSupport(LPTSTR pszPkgName) { if (pszPkgName == 0 || lstrlen(pszPkgName) == 0) return FALSE; if (Initialize() == FALSE) return FALSE; CSSPIClient Client(pszPkgName); if (Client.GetStatus() != CSSPIClient::Waiting) return FALSE; return TRUE; } //*************************************************************************** // // CSSPIClient constructor // // Parameters: // A valid SSPI package. Usually "NTLM". // // After construction has completed, GetStatus() should return // CSSPIClient::Waiting. CSSPIClient::InvalidPackage indicates that // the object will not function. // //*************************************************************************** CSSPIClient::CSSPIClient(LPTSTR pszPkgName) { // Initialize variables. // ===================== m_dwStatus = InvalidPackage; memset(&m_ClientCredential, 0, sizeof(CredHandle)); m_pPkgInfo = 0; m_pszPkgName = 0; m_cbMaxToken = 0; m_bValidCredHandle = FALSE; memset(&m_ClientContext, 0, sizeof(CtxtHandle)); m_bValidContextHandle = FALSE; // Copy package name. // =================== m_pszPkgName = Macro_CloneLPTSTR(pszPkgName); if (!m_pszPkgName) { trace(("CSSPIClient failed to construct (out of memory).\n")); m_dwStatus = InvalidPackage; return; } // Init the requested package. // =========================== SECURITY_STATUS SecStatus = 0; SecStatus = CSSPI::pVtbl->QuerySecurityPackageInfo(pszPkgName, &m_pPkgInfo); if (SecStatus != 0) { trace(("CSSPIClient fails to construct. Error = %s\n", CSSPI::TranslateError(SecStatus) )); m_dwStatus = InvalidPackage; return; } // If here, things are ok. We are waiting // for client to set login information. // ======================================== m_cbMaxToken = m_pPkgInfo->cbMaxToken; m_dwStatus = Waiting; } //*************************************************************************** // // CSSPIClient // // Destructor // //*************************************************************************** CSSPIClient::~CSSPIClient() { if (m_pPkgInfo) CSSPI::pVtbl->FreeContextBuffer(m_pPkgInfo); if (m_pszPkgName) delete [] m_pszPkgName; if (m_bValidCredHandle) CSSPI::pVtbl->FreeCredentialHandle(&m_ClientCredential); if (m_bValidContextHandle) CSSPI::pVtbl->DeleteSecurityContext(&m_ClientContext); } //*************************************************************************** // // CSSPIClient::SetLoginInfo // // Sets login info prior to beginning a login session. // pszUser, pszDomain, pszPassword must be either all NULL or all not // NULL. If NULLs are used, the current user's security context // is propagated. // // This must be the first call after constructing the CSSPIClient. // If this returns NoError, then ContinueLogin must be called next // before communicating with the server process. // // Parameters: // The user name // The NTLM domain // The cleartext password // Reserved // // Return value: // if the caller can immediately proceed to ContinueLogin(). // if one or more parameters were not valid. // if the SSPI package is not operational // // if the user/domain/password parameters are // of the wrong format. // // After successful completion, is returned and // GetStatus() will return CSSPIClient::LoginContinue. // //*************************************************************************** DWORD CSSPIClient::SetLoginInfo( IN LPTSTR pszUser, IN LPTSTR pszDomain, IN LPTSTR pszPassword, IN DWORD dwLoginFlags ) { if (m_dwStatus != Waiting) return m_dwStatus = InvalidPackage; // If the user specifies any of one {user,password,domain}, they // all must be specified. // ============================================================= if (pszUser || pszDomain || pszPassword) { if (!pszUser || !pszDomain || !pszPassword) return InvalidParameter; } // Acquire a credentials handle for subsequent calls. // ================================================== SEC_WINNT_AUTH_IDENTITY AdditionalCredentials; BOOL bSupplyCredentials = FALSE; TimeStamp Expiration; AdditionalCredentials.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; // Due to the parameter validation, we know that if // a user was specified, the other parameters have been, too. // ========================================================== if (pszUser != 0) { bSupplyCredentials = TRUE; AdditionalCredentials.User = pszUser; AdditionalCredentials.UserLength = lstrlen(pszUser); AdditionalCredentials.Password = pszPassword; AdditionalCredentials.PasswordLength = lstrlen(pszPassword); AdditionalCredentials.Domain = pszDomain; AdditionalCredentials.DomainLength = lstrlen(pszDomain); } // Get client credentials handle. // ============================== SECURITY_STATUS SecStatus = CSSPI::pVtbl->AcquireCredentialsHandle( NULL, m_pszPkgName, SECPKG_CRED_OUTBOUND, NULL, bSupplyCredentials ? &AdditionalCredentials : 0, NULL, NULL, &m_ClientCredential, &Expiration ); if (SecStatus) { trace(("AcquireCredentialsHandle() failed. Error=%s\n", CSSPI::TranslateError(SecStatus) )); return m_dwStatus = InvalidUser; } m_bValidCredHandle = TRUE; // Signal to caller that login must continue. // ========================================== m_dwStatus = LoginContinue; return NoError; } //*************************************************************************** // // CSSPIClient::SetDefaultLogin // // Convenience method to login using 'current' credentials. // // Return values same as for SetLoginInfo(). // //*************************************************************************** DWORD CSSPIClient::SetDefaultLogin(DWORD dwFlags) { return SetLoginInfo(0, 0, 0, dwFlags); } //*************************************************************************** // // CSSPIClient::ContinueLogin // // Called immediately after SetDefaultLogin() or SetLoginInfo() or // after prior calls to ContinueLogin after receiving binary tokens // from the server-side of the conversation. // // This is used for both to prepare the logon request and the // response to the challenge from the server. // // Parameters: // A token received from the server. If no // token has been received yet, then use NULL. // This is treated as read-only. // // The number of bytes in the token pointed // to by , or 0 if the above // token is NULL. // // Receives a pointer to a memory buffer // containing the token to transfer to the // server. Deallocate with operator delete. // // Points to a DWORD to receive the size // of the above token. // // Return value: // Returned when no more calls to ContinueLogin // are completed. and // are still returned in this case. // This completes the 'response' portion // in the challenge-response sequence. The server // will return an 'access denied' status through // private means. Note that does not // imply successful logon or that 'access denied' will // not occur. It merely completes the response // to the 'challenge'. // // After the login request phase (the first call to // this function) GetStatus() should return // CSSPIClient::LoginContinue, to indicate that // another call to this function will be required. // If GetStatus() CSSPIClient::LoginComplete is // returned, no more SSPI operations will occur. // Final success or denial by the server will // be privately indicated by the server. // // Internal error. No out-parms are returned in this // case. // //*************************************************************************** DWORD CSSPIClient::ContinueLogin( IN LPBYTE pInToken, IN DWORD dwInTokenSize, OUT LPBYTE *pToken, OUT DWORD *pdwTokenSize ) { // If here, we are ready to build up a token. // ========================================== SecBufferDesc OutputBufferDescriptor; SecBuffer OutputSecurityToken; ULONG uContextRequirements = 0; ULONG uContextAttributes; TimeStamp Expiration; // Set up the output buffers and out params by // default. On errors, we simply null the out params. // =================================================== OutputBufferDescriptor.cBuffers = 1; OutputBufferDescriptor.pBuffers = &OutputSecurityToken; OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION; LPBYTE pTokenBuf = new BYTE[m_cbMaxToken]; OutputSecurityToken.BufferType = SECBUFFER_TOKEN; OutputSecurityToken.cbBuffer = m_cbMaxToken; OutputSecurityToken.pvBuffer = pTokenBuf; // Build up the input buffer, if required. // ======================================= SecBufferDesc InputBufferDescriptor; SecBuffer InputSecurityToken; if (pInToken) { InputBufferDescriptor.cBuffers = 1; InputBufferDescriptor.pBuffers = &InputSecurityToken; InputBufferDescriptor.ulVersion = SECBUFFER_VERSION; InputSecurityToken.BufferType = SECBUFFER_TOKEN; InputSecurityToken.cbBuffer = dwInTokenSize; InputSecurityToken.pvBuffer = pInToken; } PSecBufferDesc pInBuf = pInToken ? &InputBufferDescriptor : 0; // Determine if this is first-time or continued call. // ================================================== PCtxtHandle pTmp = m_bValidContextHandle ? &m_ClientContext : 0; SECURITY_STATUS SecStatus = CSSPI::pVtbl->InitializeSecurityContext( &m_ClientCredential, pTmp, // Context handle pointer( NULL, // Target name (server) uContextRequirements, // Requirements 0, // Reserved SECURITY_NATIVE_DREP, // Target data representation pInBuf, // Input buffer for continued calls 0, // Reserved &m_ClientContext, // Receives client context handle &OutputBufferDescriptor, &uContextAttributes, &Expiration ); // Determine the next step. // ======================== if (SecStatus == SEC_E_OK) { *pToken = pTokenBuf; *pdwTokenSize = OutputSecurityToken.cbBuffer; m_dwStatus = LoginCompleted; return NoError; } if (SecStatus == SEC_I_CONTINUE_NEEDED) { // Set up the output parameters. // ============================= *pToken = pTokenBuf; *pdwTokenSize = OutputSecurityToken.cbBuffer; m_bValidContextHandle = TRUE; m_dwStatus = LoginContinue; return NoError; } // If here, an error occurred. // =========================== trace(("CSSPIClient::ContinueLogin failed. Status code = %s", CSSPI::TranslateError(SecStatus) )); CSSPI::pVtbl->DeleteSecurityContext(pTmp); // Deallocate useless return buffer and NULL the out-parameters. // ============================================================= delete [] pTokenBuf; *pToken = 0; *pdwTokenSize = 0; m_dwStatus = Failed; return Failed; } //*************************************************************************** // // CSSPIServer constructor // // Parameters: // The SSPI package to use, usually "NTLM". // // After construction, CSSPIServer::GetStatus() should return // CSSPIServer::Waiting. Any other value indicates that the object // is invalid and cannot be used. // //*************************************************************************** CSSPIServer::CSSPIServer(LPTSTR pszPkgName) { m_dwStatus = 0; m_cbMaxToken = 0; m_pPkgInfo = 0; m_bValidCredHandle = FALSE; m_bValidContextHandle = FALSE; memset(&m_ServerCredential, 0, sizeof(CredHandle)); memset(&m_ServerContext, 0, sizeof(CtxtHandle)); m_pszPkgName = Macro_CloneLPTSTR(pszPkgName); if (!m_pszPkgName) { trace(("CSSPIServer failed to construct (out of memory).\n")); m_dwStatus = InvalidPackage; return; } // Init the requested package. // =========================== SECURITY_STATUS SecStatus = 0; SecStatus = CSSPI::pVtbl->QuerySecurityPackageInfo(pszPkgName, &m_pPkgInfo); if (SecStatus != 0) { trace(("CSSPIServer failed to construct. Error = %s\n", CSSPI::TranslateError(SecStatus) )); m_dwStatus = InvalidPackage; return; } m_cbMaxToken = m_pPkgInfo->cbMaxToken; // Now acquire a default credentials handle for this machine. // ========================================================== TimeStamp Expiration; SecStatus = CSSPI::pVtbl->AcquireCredentialsHandle( NULL, // No principal m_pszPkgName, // Authentication package to use SECPKG_CRED_INBOUND, // We are a 'server' NULL, // No logon identifier NULL, // No pkg specific data NULL, // No GetKey function NULL, // No GetKey function arg &m_ServerCredential, // Server credential &Expiration // Expiration timestamp ); if (SecStatus != 0) { trace(("CSSPIServer failed in AcquireCredentialsHandle(). Error = %s\n", CSSPI::TranslateError(SecStatus) )); m_dwStatus = Failed; return; } m_bValidCredHandle = TRUE; m_dwStatus = Waiting; } //*************************************************************************** // // CSSPIServer destructor // //*************************************************************************** CSSPIServer::~CSSPIServer() { if (m_pPkgInfo) CSSPI::pVtbl->FreeContextBuffer(m_pPkgInfo); delete [] m_pszPkgName; if (m_bValidCredHandle) CSSPI::pVtbl->FreeCredentialHandle(&m_ServerCredential); if (m_bValidContextHandle) CSSPI::pVtbl->DeleteSecurityContext(&m_ServerContext); } //*************************************************************************** // // CSSPIServer::ContinueClientLogin // // Used to // // (1) Receive the client's logon request, and compute the challenge as // the out-parameter // // (2) Verify the response to the challenge. // // Parameters: // A read-only pointer to the response (on the second call). // NULL on the initial call. // // The number of bytes pointed to by the above pointers. // // On the first call, receives a pointer to the // challenge to send to the client. // // The size of the above challenge. // // Return values: // // Sent back on the first call to indicate that a // challenge has been computed and returned to the // client in . // // No out-params assigned. Indicates internal failure. // // Only possible on the second call. Indicates the // client was authenticated. // // Returned on the second call if the user was denied // logon. // //*************************************************************************** DWORD CSSPIServer::ContinueClientLogin( IN LPBYTE pInToken, IN DWORD dwInTokenSize, OUT LPBYTE *pToken, OUT DWORD *pdwTokenSize ) { TimeStamp Expiration; SecBufferDesc InputBufferDescriptor; SecBuffer InputSecurityToken; SecBufferDesc OutputBufferDescriptor; SecBuffer OutputSecurityToken; ULONG uContextRequirements = 0; ULONG uContextAttributes; // Build up the input buffer. // ========================== InputBufferDescriptor.cBuffers = 1; InputBufferDescriptor.pBuffers = &InputSecurityToken; InputBufferDescriptor.ulVersion = SECBUFFER_VERSION; InputSecurityToken.BufferType = SECBUFFER_TOKEN; InputSecurityToken.cbBuffer = dwInTokenSize; InputSecurityToken.pvBuffer = pInToken; // Build the output buffer descriptor. // =================================== OutputBufferDescriptor.cBuffers = 1; OutputBufferDescriptor.pBuffers = &OutputSecurityToken; OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION; LPBYTE pOutBuf = new BYTE[m_cbMaxToken]; OutputSecurityToken.BufferType = SECBUFFER_TOKEN; OutputSecurityToken.cbBuffer = m_cbMaxToken; OutputSecurityToken.pvBuffer = pOutBuf; // Set up partial or final context handle. // ======================================= PCtxtHandle pTmp = m_bValidContextHandle ? &m_ServerContext : 0; // Process the client's initial token and see what happens. // ======================================================== SECURITY_STATUS SecStatus = 0; SecStatus = CSSPI::pVtbl->AcceptSecurityContext( &m_ServerCredential, pTmp, // No context handle yet &InputBufferDescriptor, uContextRequirements, // No context requirements SECURITY_NATIVE_DREP, &m_ServerContext, &OutputBufferDescriptor, &uContextAttributes, &Expiration ); *pToken = pOutBuf; *pdwTokenSize = OutputSecurityToken.cbBuffer; if (SecStatus == SEC_I_CONTINUE_NEEDED) { m_dwStatus = LoginContinue; m_bValidContextHandle = TRUE; return LoginContinue; } if (SecStatus == SEC_E_OK) { m_bValidContextHandle = TRUE; delete [] pOutBuf; *pToken = 0; *pdwTokenSize = 0; m_dwStatus = LoginCompleted; return NoError; } // If here, an error occurred. // =========================== trace(("CSSPIClient::ContinueLogin failed. Status code = %s", CSSPI::TranslateError(SecStatus) )); *pToken = 0; *pdwTokenSize = 0; delete [] pOutBuf; if (SecStatus == SEC_E_LOGON_DENIED) { m_dwStatus = AccessDenied; return AccessDenied; } return Failed; } //*************************************************************************** // // CSSPIServer::QueryUserInfo // // Gets information about a client after authentication. // // Parameters: // Receives a pointer to the user name if TRUE is // returned. Use operator delete to deallocate. // // Return value: // TRUE if the user name was returned, FALSE if not. // //*************************************************************************** BOOL CSSPIServer::QueryUserInfo( OUT LPTSTR *pszUser // Use operator delete ) { SecPkgContext_Names Names; memset(&Names, 0, sizeof(SecPkgContext_Names)); *pszUser = 0; SECURITY_STATUS SecStatus = CSSPI::pVtbl->QueryContextAttributes( &m_ServerContext, SECPKG_ATTR_NAMES, &Names ); if (SecStatus != 0) { trace(("QueryContextAttributes() for Name fails with %s ", CSSPI::TranslateError(SecStatus) )); return FALSE; } if (pszUser) *pszUser = Macro_CloneLPTSTR(Names.sUserName); CSSPI::pVtbl->FreeContextBuffer(LPVOID(Names.sUserName)); return TRUE; } //*************************************************************************** // // CSSPIServer::IssueLoginToken // // Issues a login token for the client to use in subsequent access. // This will only succeed if the client successfully computed the // reponse to the challenge and was returned from // ContinueClientLogin. // // Parameters: // Receives the CLSID which becomes the WBEM access token. // // Return value: // NoError, Failed // //*************************************************************************** DWORD CSSPIServer::IssueLoginToken( OUT CLSID &ClsId ) { if (m_dwStatus == LoginCompleted) { if (SUCCEEDED(CoCreateGuid(&ClsId))) return NoError; else return Failed; } memset(&ClsId, 0, sizeof(CLSID)); return Failed; }