📄 ntlmssp.cpp
字号:
VxCritSec cs (m_mutex); m_channelStatus.erase (cid); }////////////////////////////////////////////////////////////////////////////// NTLMSSP::authTrailerCheck -- checks the auth-trailer of a received// PDU to ensure its requesting a authn-lvel we can cope with...//HRESULT NTLMSSP::authTrailerCheck ( const RpcPdu& pdu // received PDU ) { // Make sure its NTLM protocol... if (pdu.authTrailer()->authType != RPC_C_AUTHN_WINNT) return MAKE_HRESULT (SEVERITY_ERROR, FACILITY_RPC, RPC_S_UNKNOWN_AUTHN_SERVICE); // Make sure its CONNECT-level authn being asked for... if (pdu.authTrailer()->authLevel != RPC_C_AUTHN_LEVEL_CONNECT) return MAKE_HRESULT (SEVERITY_ERROR, FACILITY_RPC, RPC_S_UNSUPPORTED_AUTHN_LEVEL); return S_OK; }////////////////////////////////////////////////////////////////////////////// NTLMSSP::serverRequestValidate -- validate the auth-trailer on a// received RPC REQUEST PDU, which usually contains the 16-byte// 'token' and nothing else...//HRESULT NTLMSSP::serverRequestValidate ( int cid, // channel ID const RpcPdu& reqPdu, // rcvd REQUEST packet RpcPdu& reply // reply to be sent ) { if (! reqPdu.isRequest ()) return E_FAIL; // Is there an auth-trailer? if (reqPdu.authLen ()) { rpc_cn_auth_tlr_t authTlrOut; const rpc_cn_auth_tlr_t* pAuthTlrIn = reqPdu.authTrailer (); // Look for familiar token in REQUEST packets.. if ((reqPdu.authLen () == TOKEN_LEN) && (memcmp (pAuthTlrIn->authValue, s_token, TOKEN_LEN) == 0)) { // Set outbound pre-amble fields... authTlrOut.authType = RPC_C_AUTHN_WINNT; authTlrOut.authLevel = RPC_C_AUTHN_LEVEL_CONNECT; authTlrOut.stubPadLen = 0; authTlrOut.reserved = 0; authTlrOut.key = pAuthTlrIn->key; // Copy 'token' into place... memcpy (authTlrOut.authValue, s_token, TOKEN_LEN); reply.authTrailerAppend (authTlrOut, TOKEN_LEN); } } return S_OK; }////////////////////////////////////////////////////////////////////////////// serverBindValidate -- performs authentication checks on incoming// BIND (or ALTER-CONTEXT) PDUs received by a server connection. The// incoming PDU will contain the requesting machine and domain-name.//// Currently these fields are ignored, as we don't recognise NT// domains or do anything with them, so we simply respond by returning// a challenge. The difference between authenticating or not occurs// when the AUTH3 PDU arrives, which we process in the method// serverAuth3Validate().//// If we require authenticated connections, but we receive a BIND// without an auth-trailer, then it must also be rejected, as we// have been configured not to handle un-authenticated// connections. Conversely, if we don't *require* authentication, but// there is a request there in the packet, we still honour it by// adding a challenge, i.e. we move up to the client's level of authn,// but when his AUTH3 arrives we will simply accept it.//HRESULT NTLMSSP::serverBindValidate ( int cid, // channel ID const RpcPdu& bindPdu, // rcvd BIND packet RpcPdu& reply // reply to be sent ) { TRACE_CALL; // Only process BIND packets... if (! (bindPdu.isBind ())) return E_FAIL; // Get current status... VxCritSec cs (m_mutex); HRESULT status = m_channelStatus [cid]; // Check what level we require... switch (m_authnLevel) { case RPC_C_AUTHN_LEVEL_NONE: case RPC_C_AUTHN_LEVEL_CONNECT: // This case handles both NONE and CONNECT levels, but treats // NONE as a special case when a received BIND PDU has an // auth-trailer, in that we respond to it with a challenge // (just like we do when we require authentication), but we // effectively ignore the response in the next AUTH3 packet. // // CONNECT-level is supported if the user/application has // registered some user/password combinations, so we *do* // require some authentication to be present, so if there's // none, we reject the PDU. // // For CONNECT level, we must defer the status-change (to S_OK // from ACCESS-DENIED) to the time we get the response to our // challenge, whereas for the NONE case we can simply let the // connection continue at S_OK... if (m_authnLevel == RPC_C_AUTHN_LEVEL_CONNECT) status = E_ACCESSDENIED; else status = S_OK; // Now check there is a trailer to process... if (bindPdu.authLen ()) { // Check the auth-trailer for correctness... HRESULT hr = authTrailerCheck (bindPdu); if (FAILED (hr)) status = hr; else { // Okay -- now we can go ahead and prepare the reply // trailer for sending the challenge... rpc_cn_auth_tlr_t authTlrOut; size_t lenTlr=0; DREP drep = bindPdu.drep (); const rpc_cn_auth_tlr_t* pAuthTlrIn = bindPdu.authTrailer (); // Set outbound pre-amble fields... authTlrOut.authType = RPC_C_AUTHN_WINNT; authTlrOut.authLevel = RPC_C_AUTHN_LEVEL_CONNECT; authTlrOut.stubPadLen = 0; authTlrOut.reserved = 0; authTlrOut.key = pAuthTlrIn->key; // Find request and challenge structures in PDUs... request* preq = (request*) pAuthTlrIn->authValue; challenge* pch = (challenge*) authTlrOut.authValue; // Find received sequence-number ULONG seq = preq->sequence; ndr_make_right (seq, drep); // Normal client request -- fill in codename strcpy (pch->ntlmssp, "NTLMSSP"); // Look for repeated sequence-number 3 in ALTER-CONTEXT PDUs. If // its one of these then we can get away without putting an // auth-trailer on the response, and continue... if ((seq == 3) && (bindPdu.packetType () == RPC_CN_PKT_ALTER_CONTEXT)) status = S_OK; else { // Increment sequence-number... pch->sequence = seq + 1; ndr_make_right (pch->sequence, drep); // Fill in fields and NDR them pch->magic1 = NTLM_MAGIC1; ndr_make_right (pch->magic1, drep); pch->magic2 = NTLM_MAGIC2_BIND; ndr_make_right (pch->magic2, drep); memcpy (pch->chal, s_defaultChallenge, 8); pch->mbz1 = 0; pch->mbz2 = 0; pch->mbz3 = 0; lenTlr = sizeof (NTLMSSP::challenge); reply.authTrailerAppend (authTlrOut, lenTlr); } } } break; default: // Other levels we simply don't handle... status = E_ACCESSDENIED; } m_channelStatus [cid] = status; return status; }////////////////////////////////////////////////////////////////////////////void NTLMSSP::ndrUnihdr (UNIHDR* puh, DREP drep) { ndr_make_right (puh->len, drep); ndr_make_right (puh->max, drep); ndr_make_right (puh->offset, drep); }////////////////////////////////////////////////////////////////////////////// userAdd -- adds a user to the user-table, storing the 16-byte// LM-hash rather than the plain-text password...// void NTLMSSP::userAdd (const char* userName, const char* userPasswd) { // Compute LM-hash of plain-text password... unsigned char ntlmPasswd [16]; // Mangle the user-password into LanMan format (14 chars max, upper case) memset (ntlmPasswd, '\0', sizeof (ntlmPasswd)); strncpy ((char*) ntlmPasswd, userPasswd, sizeof (ntlmPasswd)); ntlmPasswd [14] = '\0'; strupper (ntlmPasswd); // Calculate the SMB (lanman) hash function of the password NTLMSSP::lmhash ntlmHash; memset (&ntlmHash, 0, sizeof (ntlmHash)); NTLMSSP::DES::encrypt16 (ntlmPasswd, ntlmHash.data); // Save the hash into the user-table... s_userTable [userName] = ntlmHash; // Erase the hash and plain-text from the stack memset (&ntlmHash, 0, 16); memset (ntlmPasswd, 0, 16); }////////////////////////////////////////////////////////////////////////////// encrypt -- encrypts the user's password-hash with the 8-byte// challenge and produces a 24-byte response...//void NTLMSSP::encrypt ( const BYTE* pwdHash, // the password-hash const BYTE* challenge, // the challenge BYTE* p24 // output ) { BYTE p21 [21]; memset (p21,'\0',21); memcpy (p21, pwdHash, 16); NTLMSSP::DES::encrypt24 (p21, const_cast<BYTE*> (challenge), p24); }////////////////////////////////////////////////////////////////////////////// serverAuth3Validate -- this function is called when an AUTH3 PDU is// received at the server-side of a connection. This will happen when// the client has attempted to open an authenticated connection to// this machine, and we have sent it a challenge. What we get back is// the response to that challenge, which must match our calculated// response if we are bothered about authentication at all. If we// aren't bothered, (i.e. the authn-level is set to NONE) then we// simply let the AUTH3 PDU 'pass through' as if it were okay...//HRESULT NTLMSSP::serverAuth3Validate ( int cid, // channel ID const RpcPdu& auth3Pdu // rcvd AUTH3 packet ) { // Get current status... VxCritSec cs (m_mutex); HRESULT status = m_channelStatus [cid]; // Check how much authn we require... switch (m_authnLevel) { case RPC_C_AUTHN_LEVEL_NONE: // No authn required, we can let this one through... status = S_OK; break; case RPC_C_AUTHN_LEVEL_CONNECT: // Connect-level is supported if the user/application has // registered some user/password combinations, so we must // continue processing. Check the auth-trailer for // correctness... status = authTrailerCheck (auth3Pdu); if (SUCCEEDED (status)) { // Check there is some data to deal with -- if we get to // here then we have received an AUTH3 packet and so there // *must* be some, if not we return an error-value and // effectively reject the packet... if (auth3Pdu.authLen () == 0) status = E_ACCESSDENIED; else { // Find the start of the trailer data... const rpc_cn_auth_tlr_t* pAuthTlr = auth3Pdu.authTrailer (); response* phdr = (response*) pAuthTlr->authValue; // NDR the important fields... ndr_make_right (phdr->sequence, auth3Pdu.drep ()); ndrUnihdr (&phdr->domainName, auth3Pdu.drep ()); ndrUnihdr (&phdr->userName, auth3Pdu.drep ()); ndrUnihdr (&phdr->machineName, auth3Pdu.drep ()); ndr_make_right (phdr->magic2, auth3Pdu.drep ()); int textBytes = phdr->domainName.len + phdr->userName.len + phdr->machineName.len; USHORT* pwsText = (USHORT*) (phdr+1); for (int t=0; t < textBytes / 2; ++t) ndr_make_right (pwsText [t], auth3Pdu.drep ()); // Extract user-name as ASCII string... char userName [32]; int n = phdr->userName.len / 2; pwsText += (phdr->domainName.len / 2); for (int i=0; i < n; ++i) userName [i] = (char) pwsText [i]; userName [n] = 0; // Find the 'response' data block in the PDU BYTE* pdata = ((BYTE*) (phdr+1)) + textBytes; // Calculate what the response should be... BYTE calculatedResult[24]; memset (calculatedResult, 0, 24); encrypt (s_userTable [userName].data, s_defaultChallenge, calculatedResult); // Did the encrypted response come out right? if (memcmp (pdata, calculatedResult, 24) != 0) { S_INFO (LOG_AUTH, "AUTH3:access denied:" << userName); status = E_ACCESSDENIED; } else { S_INFO (LOG_AUTH, "AUTH3:access granted:" << userName); status = S_OK; } } } break; case RPC_C_AUTHN_LEVEL_CALL: case RPC_C_AUTHN_LEVEL_PKT: case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY: case RPC_C_AUTHN_LEVEL_PKT_PRIVACY: // These levels are not supported... status = MAKE_HRESULT (SEVERITY_ERROR, FACILITY_RPC, RPC_S_UNSUPPORTED_AUTHN_LEVEL); case RPC_C_AUTHN_LEVEL_DEFAULT: default: // Unknown errors! COM_ASSERT (0); status = E_FAIL; } m_channelStatus [cid] = status; return status; }////////////////////////////////////////////////////////////////////////////// clientAuthnRequestAdd -- empty stub for now.//HRESULT NTLMSSP::clientAuthnRequestAdd ( int channelId, RpcPdu& bindPdu ) { return S_OK; }////////////////////////////////////////////////////////////////////////////// clientAuthnResponse -- empty stub for now.//HRESULT NTLMSSP::clientAuthnResponse ( int channelId, const RpcPdu& bindAckPdu, bool* pbSendAuth3, RpcPdu& auth3Pdu ) { *pbSendAuth3 = false; return S_OK; }////////////////////////////////////////////////////////////////////////////// clientAuthn -- empty stub for now.//HRESULT NTLMSSP::clientAuthn ( int channelId, RpcPdu& reqPdu ) { return S_OK; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -