/*++ Copyright (c) 1998 Microsoft Corporation Module Name: cred.cxx Abstract: Shared memory data structures for digest sspi package. Author: Adriaan Canter (adriaanc) 01-Aug-1998 --*/ #include "include.hxx" ////////////////////////////////////////////////////////////////////// // // CEntry Functions // ////////////////////////////////////////////////////////////////////// //-------------------------------------------------------------------- // CEntry::Free // BUGBUG - inline this. //-------------------------------------------------------------------- DWORD CEntry::Free(CMMFile *pMMFile, CEntry *pEntry) { BOOL bFree = pMMFile->FreeEntry(pEntry); return (bFree ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR); } //-------------------------------------------------------------------- // CEntry::GetNext //-------------------------------------------------------------------- CEntry* CEntry::GetNext(CEntry *pEntry) { if (pEntry->dwNext) return (CEntry*) OFFSET_TO_POINTER(g_pHeap, pEntry->dwNext); return NULL; } //-------------------------------------------------------------------- // CEntry::GetPrev //-------------------------------------------------------------------- CEntry* CEntry::GetPrev(CEntry *pEntry) { if (pEntry->dwPrev) return (CEntry*) OFFSET_TO_POINTER(g_pHeap, pEntry->dwPrev); return NULL; } ////////////////////////////////////////////////////////////////////// // // CSess Functions // ////////////////////////////////////////////////////////////////////// //-------------------------------------------------------------------- // CSess::Create //-------------------------------------------------------------------- CSess* CSess::Create(CMMFile *pMMFile, LPSTR szAppCtx, LPSTR szUserCtx, BOOL fHTTP) { DIGEST_ASSERT(pMMFile); // Unaligned Sess lengths. DWORD cbAppCtx = szAppCtx ? strlen(szAppCtx) + 1 : 0; DWORD cbUserCtx = szUserCtx ? strlen(szUserCtx) + 1 : 0; // Aligned Sess lengths. DWORD cbStructAligned = ROUNDUPDWORD(sizeof(CSess)); DWORD cbAppCtxAligned = ROUNDUPDWORD(cbAppCtx); DWORD cbUserCtxAligned = ROUNDUPDWORD(cbUserCtx); // Total number of required bytes (aligned). DWORD cbEntryAligned = cbStructAligned + cbAppCtxAligned + cbUserCtxAligned; // Allocate from mem map. CSess *pSess = (CSess*) pMMFile->AllocateEntry(cbEntryAligned); if (!pSess) { DIGEST_ASSERT(FALSE); goto exit; } DWORD cbCurrentOffset; cbCurrentOffset = cbStructAligned; pSess->dwAppCtx = pSess->dwUserCtx = 0; // AppCtx if (szAppCtx) { memcpy(OFFSET_TO_POINTER(pSess, cbCurrentOffset), szAppCtx, cbAppCtx); pSess->dwAppCtx = cbCurrentOffset; cbCurrentOffset += cbAppCtxAligned; } // UserCtx if (szUserCtx) { memcpy(OFFSET_TO_POINTER(pSess, cbCurrentOffset), szUserCtx, cbUserCtx); pSess->dwUserCtx = cbCurrentOffset; } // No need to advance cbCurrentOffset. pSess->cbSess = cbEntryAligned; pSess->dwCred = 0; pSess->dwSig = SIG_SESS; pSess->fHTTP = fHTTP; pSess->dwPrev = 0; pSess->dwNext = 0; exit: return pSess; } //-------------------------------------------------------------------- // CSess::GetAppCtx //-------------------------------------------------------------------- LPSTR CSess::GetAppCtx(CSess *pSess) { if (pSess->dwAppCtx) return (LPSTR) OFFSET_TO_POINTER(pSess, pSess->dwAppCtx); else return NULL; } //-------------------------------------------------------------------- // CSess::GetUserCtx //-------------------------------------------------------------------- LPSTR CSess::GetUserCtx(CSess *pSess) { if (pSess->dwUserCtx) return (LPSTR) OFFSET_TO_POINTER(pSess, pSess->dwUserCtx); else return NULL; } //-------------------------------------------------------------------- // CSess::GetCtx // Allocates a context string in local heap. //-------------------------------------------------------------------- LPSTR CSess::GetCtx(CSess *pSess) { DIGEST_ASSERT(pSess); LPSTR szAppCtx, szUserCtx, szCtx; DWORD cbAppCtx, cbUserCtx, cbCtx; szAppCtx = GetAppCtx(pSess); szUserCtx = GetUserCtx(pSess); cbAppCtx = szAppCtx ? strlen(szAppCtx) : 0; cbUserCtx = szUserCtx ? strlen(szUserCtx) : 0; cbCtx = cbAppCtx + sizeof(':') + cbUserCtx + sizeof('\0'); szCtx = new CHAR[cbCtx]; if (!szCtx) { DIGEST_ASSERT(FALSE); return NULL; } // "appctx:userctx\0" // bugbug - macro for sizeof -1 memcpy(szCtx, szAppCtx, cbAppCtx); memcpy(szCtx + cbAppCtx, ":", sizeof(":") - 1); memcpy(szCtx + cbAppCtx + sizeof(":") - 1, szUserCtx, cbUserCtx); memcpy(szCtx + cbAppCtx + sizeof(":") - 1 + cbUserCtx, "\0", sizeof("\0") - 1); return szCtx; } //-------------------------------------------------------------------- // CSess::CtxMatch //-------------------------------------------------------------------- BOOL CSess::CtxMatch(CSess *pSess1, CSess *pSess2) { DIGEST_ASSERT(pSess1 && pSess2); LPSTR szAppCtx1, szAppCtx2, szUserCtx1, szUserCtx2; szAppCtx1 = CSess::GetAppCtx(pSess1); szAppCtx2 = CSess::GetAppCtx(pSess2); szUserCtx1 = CSess::GetUserCtx(pSess1); szUserCtx2 = CSess::GetUserCtx(pSess2); // If both AppCtx values are NULL or // are equal to same string. if ((!szAppCtx1 && !szAppCtx2) || ((szAppCtx1 && szAppCtx2) && !strcmp(szAppCtx1, szAppCtx2))) { // And both UserCtx values are NULL or // are equal to same string. if ((!szUserCtx1 && !szUserCtx2) || ((szUserCtx1 && szUserCtx2) && !strcmp(szUserCtx1, szUserCtx2))) { // Credentials are shareable. return TRUE; } } // Otherwise creds are not shareable. return FALSE; } //-------------------------------------------------------------------- // CSess::GetCred //-------------------------------------------------------------------- CCred *CSess::GetCred(CSess* pSess) { if (!pSess->dwCred) return NULL; return (CCred*) OFFSET_TO_POINTER(g_pHeap, pSess->dwCred); } //-------------------------------------------------------------------- // CSess::SetCred //-------------------------------------------------------------------- CCred *CSess::SetCred(CSess* pSess, CCred* pCred) { if (!pCred) pSess->dwCred = 0; else pSess->dwCred = POINTER_TO_OFFSET(g_pHeap, pCred); return pCred; } ////////////////////////////////////////////////////////////////////// // // CList Functions // ////////////////////////////////////////////////////////////////////// //-------------------------------------------------------------------- // CList::CList //-------------------------------------------------------------------- CList::CList() { _pHead = NULL; _pCur = NULL; _pdwOffset = NULL; } //-------------------------------------------------------------------- // CList::Init //-------------------------------------------------------------------- CEntry* CList::Init(LPDWORD pdwOffset) { DIGEST_ASSERT(pdwOffset) _pdwOffset = pdwOffset; if (*pdwOffset) { _pHead = (CEntry*) OFFSET_TO_POINTER(g_pHeap, *pdwOffset); _pCur = _pHead; } return _pHead; } //-------------------------------------------------------------------- // CList::Seek //-------------------------------------------------------------------- CEntry* CList::Seek() { // DIGEST_ASSERT(_pHead); _pHead = (CEntry*) OFFSET_TO_POINTER(g_pHeap, *_pdwOffset); _pCur = _pHead; return _pCur; } //-------------------------------------------------------------------- // CList::GetNext //-------------------------------------------------------------------- CEntry* CList::GetNext() { if (! *_pdwOffset) return NULL; CEntry *pEntry = _pCur; if (_pCur) _pCur = _pCur->dwNext ? (CEntry*) OFFSET_TO_POINTER(g_pHeap, _pCur->dwNext) : NULL; return pEntry; } //-------------------------------------------------------------------- // CList::GetPrev //-------------------------------------------------------------------- CEntry* CList::GetPrev() { if (! *_pdwOffset) return NULL; CEntry *pEntry = _pCur; if (_pCur) _pCur = _pCur->dwPrev ? (CEntry*) OFFSET_TO_POINTER(g_pHeap, _pCur->dwPrev) : NULL; return pEntry; } //-------------------------------------------------------------------- // CList::Insert //-------------------------------------------------------------------- CEntry* CList::Insert(CEntry *pEntry) { // BUGBUG - assert pnext pprev are null DIGEST_ASSERT(pEntry && (pEntry->dwPrev == pEntry->dwNext == 0)); if (!_pHead) { _pHead = pEntry; } else { pEntry->dwNext = POINTER_TO_OFFSET(g_pHeap, _pHead); _pHead->dwPrev = POINTER_TO_OFFSET(g_pHeap, pEntry); _pHead = pEntry; } *_pdwOffset = POINTER_TO_OFFSET(g_pHeap, pEntry); return pEntry; } //-------------------------------------------------------------------- // CList::DeLink //-------------------------------------------------------------------- CEntry* CList::DeLink(CEntry *pEntry) { DIGEST_ASSERT(pEntry); if (pEntry == _pHead) { _pHead = (CEntry*) OFFSET_TO_POINTER(g_pHeap, pEntry->dwNext); *_pdwOffset = POINTER_TO_OFFSET(g_pHeap, _pHead); } else { CEntry *pPrev; pPrev = (CEntry*) OFFSET_TO_POINTER(g_pHeap, pEntry->dwPrev); pPrev->dwNext = pEntry->dwNext; } if (pEntry->dwNext) { CEntry *pNext; pNext = (CEntry*) OFFSET_TO_POINTER(g_pHeap, pEntry->dwNext); pNext->dwPrev = pEntry->dwPrev; } if (_pCur == pEntry) { _pCur = (CEntry*) OFFSET_TO_POINTER(g_pHeap, pEntry->dwNext); } return pEntry; } ////////////////////////////////////////////////////////////////////// // // CCred Functions // ////////////////////////////////////////////////////////////////////// //-------------------------------------------------------------------- // CCred::Create //-------------------------------------------------------------------- CCred* CCred::Create(CMMFile *pMMFile, LPSTR szHost, LPSTR szRealm, LPSTR szUser, LPSTR szPass, LPSTR szNonce, LPSTR szCNonce) { // BUGBUG - assert strings non-null. Nonce can be NULL. DIGEST_ASSERT(pMMFile && szRealm && szUser && szPass); CSecureStr EncryptedPassword; if (szPass && !EncryptedPassword.SetData(szPass)) return NULL; // Unaligned string sizes. DWORD cbRealm = szRealm ? strlen(szRealm) + 1 : 0; DWORD cbUser = szUser ? strlen(szUser) + 1 : 0; DWORD cbPass = szPass ? EncryptedPassword.GetStrLen() : 0; // Aligned string sizes. DWORD cbStructAligned = ROUNDUPDWORD(sizeof(CCred)); DWORD cbRealmAligned = ROUNDUPDWORD(cbRealm); DWORD cbUserAligned = ROUNDUPDWORD(cbUser); DWORD cbPassAligned = ROUNDUPDWORD(cbPass); // Total number of required bytes (aligned). DWORD cbEntryAligned = cbStructAligned + cbRealmAligned + cbUserAligned + cbPassAligned; // Allocate cred and nonce from memmap // BUGBUG - MASKING PNONCE // BUGBUG - no, I'm not. CCred *pCred; CNonce *pNonce, *pCNonce; pNonce = pCNonce = NULL; // Allocate a credential. pCred = (CCred*) pMMFile->AllocateEntry(cbEntryAligned); if (!pCred) { DIGEST_ASSERT(FALSE); goto exit; } DWORD cbCurrentOffset; cbCurrentOffset = cbStructAligned; // Realm. memcpy(OFFSET_TO_POINTER(pCred, cbCurrentOffset), szRealm, cbRealm); pCred->dwRealm = cbCurrentOffset; cbCurrentOffset += cbRealmAligned; // User memcpy(OFFSET_TO_POINTER(pCred, cbCurrentOffset), szUser, cbUser); pCred->dwUser = cbCurrentOffset; cbCurrentOffset += cbUserAligned; // Pass memcpy(OFFSET_TO_POINTER(pCred, cbCurrentOffset), EncryptedPassword.GetPtr(), cbPass); pCred->dwPass = cbCurrentOffset; pCred->cbPass = cbPass; pCred->cbCred = cbEntryAligned; pCred->tStamp = GetTickCount(); pCred->dwSig = SIG_CRED; pCred->dwPrev = NULL; pCred->dwNext = NULL; pCred->dwNonce = 0; pCred->dwCNonce = 0; // Allocate nonce and client nonce if specified. if (szNonce) CCred::SetNonce(pMMFile, pCred, szHost, szNonce, SERVER_NONCE); if (szCNonce) CCred::SetNonce(pMMFile, pCred, szHost, szCNonce, CLIENT_NONCE); exit: return pCred; } //-------------------------------------------------------------------- // CCred::GetRealm //-------------------------------------------------------------------- LPSTR CCred::GetRealm(CCred* pCred) { DIGEST_ASSERT(pCred); if (pCred->dwRealm) return (LPSTR) OFFSET_TO_POINTER(pCred, pCred->dwRealm); else return NULL; } //-------------------------------------------------------------------- // CCred::GetUser //-------------------------------------------------------------------- LPSTR CCred::GetUser(CCred* pCred) { DIGEST_ASSERT(pCred); if (pCred->dwUser) return (LPSTR) OFFSET_TO_POINTER(pCred, pCred->dwUser); else return NULL; } //-------------------------------------------------------------------- // CCred::GetPass //-------------------------------------------------------------------- LPSTR CCred::GetPass(CCred* pCred) { DIGEST_ASSERT(pCred); if (pCred->dwPass) { CSecureStr EncryptedPassword( (LPSTR) OFFSET_TO_POINTER(pCred, pCred->dwPass), pCred->cbPass, false); return EncryptedPassword.GetUnencryptedString(); } else return NULL; } //-------------------------------------------------------------------- // CCred::SetNonce //-------------------------------------------------------------------- VOID CCred::SetNonce(CMMFile *pMMFile, CCred* pCred, LPSTR szHost, LPSTR szNonce, DWORD dwType) { DIGEST_ASSERT(pCred && szNonce); // First determine if a nonce for the specified host // already exists and delete it if it does. CList NonceList; NonceList.Init(dwType == SERVER_NONCE ? &pCred->dwNonce : &pCred->dwCNonce); CNonce *pNonce; while (pNonce = (CNonce*) NonceList.GetNext()) { if (CNonce::IsHostMatch(pNonce, szHost)) { NonceList.DeLink(pNonce); CEntry::Free(pMMFile, pNonce); break; } } // Create a CNonce object and insert it into the list. pNonce = CNonce::Create(pMMFile, szHost, szNonce); if (pNonce) { NonceList.Seek(); NonceList.Insert(pNonce); } else { DIGEST_ASSERT(FALSE); } } //-------------------------------------------------------------------- // CCred::GetNonce //-------------------------------------------------------------------- CNonce *CCred::GetNonce(CCred* pCred, LPSTR szHost, DWORD dwType) { CNonce *pNonce; CList NonceList; NonceList.Init(dwType == SERVER_NONCE ? &pCred->dwNonce : &pCred->dwCNonce); while (pNonce = (CNonce*) NonceList.GetNext()) { if (CNonce::IsHostMatch(pNonce, szHost)) break; } return pNonce; } //-------------------------------------------------------------------- // CCred::Free //-------------------------------------------------------------------- VOID CCred::Free(CMMFile *pMMFile, CSess *pSess, CCred *pCred) { CNonce *pNonce; CList NonceList; // Free up server nonce. NonceList.Init(&pCred->dwNonce); while (pNonce = (CNonce*) NonceList.GetNext()) { NonceList.DeLink(pNonce); CEntry::Free(pMMFile, pNonce); } // Free up client nonce. NonceList.Init(&pCred->dwCNonce); while (pNonce = (CNonce*) NonceList.GetNext()) { NonceList.DeLink(pNonce); CEntry::Free(pMMFile, pNonce); } // Remove credential from session's list. CList CredList; CredList.Init(&pSess->dwCred); CredList.DeLink(pCred); CEntry::Free(pMMFile, pCred); } ////////////////////////////////////////////////////////////////////// // // CNonce Functions // ////////////////////////////////////////////////////////////////////// //-------------------------------------------------------------------- // CNonce::Create //-------------------------------------------------------------------- CNonce* CNonce::Create(CMMFile *pMMFile, LPSTR szHost, LPSTR szNonce) { DIGEST_ASSERT(pMMFile && szNonce); // Unaligned string sizes. DWORD cbNonce = szNonce ? strlen(szNonce) + 1 : 0; DWORD cbHost = szHost ? strlen(szHost) + 1 : 0; // Aligned string sizes. DWORD cbStructAligned = ROUNDUPDWORD(sizeof(CNonce)); DWORD cbNonceAligned = ROUNDUPDWORD(cbNonce); DWORD cbHostAligned = ROUNDUPDWORD(cbHost); // Total number of required bytes (aligned). DWORD cbEntryAligned = cbStructAligned + cbHostAligned + cbNonceAligned; // Allocate from mem map. CNonce *pNonce = (CNonce*) pMMFile->AllocateEntry(cbEntryAligned); // Failed to allocate. if (!pNonce) { DIGEST_ASSERT(FALSE); goto exit; } DWORD cbCurrentOffset; cbCurrentOffset = cbStructAligned; // Host. if (szHost) { memcpy(OFFSET_TO_POINTER(pNonce, cbCurrentOffset), szHost, cbHost); pNonce->dwHost = cbCurrentOffset; cbCurrentOffset += cbHostAligned; } else { pNonce->dwHost = 0; } // Nonce. memcpy(OFFSET_TO_POINTER(pNonce, cbCurrentOffset), szNonce, cbNonce); pNonce->dwNonce = cbCurrentOffset; cbCurrentOffset += cbNonceAligned; pNonce->cbNonce = cbEntryAligned; pNonce->cCount = 0; pNonce->dwSig = SIG_NONC; pNonce->dwPrev = 0; pNonce->dwNext = 0; exit: return pNonce; } //-------------------------------------------------------------------- // CNonce::GetNonce //-------------------------------------------------------------------- LPSTR CNonce::GetNonce(CNonce* pNonce) { if (pNonce && pNonce->dwNonce) return (LPSTR) OFFSET_TO_POINTER(pNonce, pNonce->dwNonce); else return NULL; } //-------------------------------------------------------------------- // CNonce::GetCount // bugbug - what if no nonce? //-------------------------------------------------------------------- DWORD CNonce::GetCount(CNonce* pNonce) { DIGEST_ASSERT(pNonce); return pNonce->cCount; } //-------------------------------------------------------------------- // CNonce::IsHostMatch //-------------------------------------------------------------------- BOOL CNonce::IsHostMatch(CNonce *pNonce, LPSTR szHost) { if (szHost) { if (pNonce->dwHost && !strcmp(szHost, (LPSTR) OFFSET_TO_POINTER(pNonce, pNonce->dwHost))) return TRUE; return FALSE; } if (!pNonce->dwHost) return TRUE; return FALSE; } ////////////////////////////////////////////////////////////////////// // // CCredInfo Functions // ////////////////////////////////////////////////////////////////////// //-------------------------------------------------------------------- // CCredInfo::CCredInfo //-------------------------------------------------------------------- CCredInfo::CCredInfo(CCred* pCred, LPSTR szHost) { DIGEST_ASSERT(pCred && (pCred->dwPass > pCred->dwUser) && (pCred->dwUser > pCred->dwRealm)); CCredInfo::szHost = NewString(szHost); szRealm = NewString(CCred::GetRealm(pCred)); szUser = NewString(CCred::GetUser(pCred)); LPSTR szPass = CCred::GetPass(pCred); if (szPass) { BOOL fSetDataSuccess = Password.SetData(szPass); SecureZeroMemory(szPass, strlen(szPass)); delete [] szPass; if (!fSetDataSuccess) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; return; } } // Get server nonce. This is always required. // BUGBUG - what about pre-loading? CNonce *pNonce; pNonce = CCred::GetNonce(pCred, szHost, SERVER_NONCE); if (!pNonce) { dwStatus = ERROR_INVALID_PARAMETER; return; } szNonce = NewString(CNonce::GetNonce(pNonce)); cCount = pNonce->cCount; pNonce = CCred::GetNonce(pCred, szHost, CLIENT_NONCE); if (pNonce) szCNonce = NewString(CNonce::GetNonce(pNonce)); else szCNonce = NULL; tStamp = pCred->tStamp; pPrev = NULL; pNext = NULL; dwStatus = ERROR_SUCCESS; } //-------------------------------------------------------------------- // CCredInfo::CCredInfo // BUGBUG - special casing for NULLs, especially szNonce. //-------------------------------------------------------------------- CCredInfo::CCredInfo(LPSTR szHost, LPSTR szRealm, LPSTR szUser, LPSTR szPass, LPSTR szNonce, LPSTR szCNonce) { CCredInfo::szHost = NewString(szHost); CCredInfo::szRealm = NewString(szRealm); CCredInfo::szUser = NewString(szUser); CCredInfo::Password.SetData(szPass); CCredInfo::szNonce = NewString(szNonce); CCredInfo::szCNonce = NewString(szCNonce); cCount = 0; tStamp = 0; pPrev = NULL; pNext = NULL; dwStatus = ERROR_SUCCESS; } //-------------------------------------------------------------------- // CCredInfo::~CCredInfo //-------------------------------------------------------------------- CCredInfo::~CCredInfo() { if (szHost) delete [] szHost; if (szRealm) delete [] szRealm; if (szUser) delete [] szUser; Password.Free(); if (szNonce) delete [] szNonce; if (szCNonce) delete [] szCNonce; }