xbox-kernel/private/ntos/dm/xbdm/dmsecure.c
2020-09-30 17:17:25 +02:00

538 lines
14 KiB
C

/*
*
* dmsecure.c
*
* Connection security functions
*
*/
#include "dmp.h"
static ULONG rgN[8] = {
0x283C481D,
0x9AD82AA1,
0x85A5E1F9,
0x1B23963C,
0xF70B4975,
0xDFDC02C7,
0xF29176FC,
0x6B04BD38
};
static HANDLE hUserList;
RTL_CRITICAL_SECTION csUserList;
ULARGE_INTEGER g_luAdminPasswd;
BOOL g_fAdminPasswd;
typedef struct _DMUSER {
ULARGE_INTEGER luPasswd;
DWORD dwPrivileges;
BOOL fHasPasswd;
} DMUSER, *PDMUSER;
static OBJECT_TYPE obtUser = {
DmAllocatePoolWithTag,
DmFreePool,
NULL,
NULL,
NULL,
&kevtNull,
'SUmd'
};
void XBCEncryptCore(PULARGE_INTEGER pluKey, PULARGE_INTEGER pluBlock)
{
int i;
for(i = 0; i < 8; ++i) {
pluBlock->LowPart ^= (pluBlock->HighPart >> 5) + rgN[i] +
(~pluBlock->HighPart << 6) + (pluBlock->HighPart ^ pluKey->LowPart);
pluBlock->HighPart ^= (~pluBlock->LowPart >> 5) + rgN[7-i] +
(pluBlock->LowPart << 6) + (pluBlock->LowPart ^ pluKey->HighPart);
}
}
void XBCDecryptCore(PULARGE_INTEGER pluKey, PULARGE_INTEGER pluBlock)
{
int i;
for(i = 0; i < 8; ++i) {
pluBlock->HighPart ^= (~pluBlock->LowPart >> 5) + rgN[i] +
(pluBlock->LowPart << 6) + (pluBlock->LowPart ^ pluKey->HighPart);
pluBlock->LowPart ^= (pluBlock->HighPart >> 5) + rgN[7-i] +
(~pluBlock->HighPart << 6) + (pluBlock->HighPart ^ pluKey->LowPart);
}
}
void XBCHashBlock(PULARGE_INTEGER pluHash, PULARGE_INTEGER pluData)
{
/* We don't use Davies-Meyer directly, we use an analogous formula:
* H(i) = E(H(i-1),M(i)) X M(i)
*/
ULARGE_INTEGER luTemp;
luTemp = *pluData;
XBCEncryptCore(pluHash, &luTemp);
pluHash->LowPart = pluData->LowPart ^ luTemp.LowPart;
pluHash->HighPart = pluData->HighPart ^ luTemp.HighPart;
}
void XBCHashData(PULARGE_INTEGER pluHash, const BYTE *pb, ULONG cb)
{
ULARGE_INTEGER lu;
/* Process the 8-byte chunks first */
while(cb >= 8) {
memcpy(&lu, pb, 8);
pb += 8;
cb -= 8;
XBCHashBlock(pluHash, &lu);
}
if(cb) {
memset(&lu, 0, sizeof lu);
memcpy(&lu, pb, cb);
XBCHashBlock(pluHash, &lu);
}
}
void XBCCross(PULARGE_INTEGER pluKey, PULARGE_INTEGER pluData,
PULARGE_INTEGER pluResult)
{
ULARGE_INTEGER luTemp;
/* Compute H(K, H(K, M)) where K is the key and M is the data */
pluResult->QuadPart = 0x2718281831415926;
XBCHashBlock(pluResult, pluKey);
luTemp = *pluResult;
XBCHashBlock(&luTemp, pluData);
XBCHashBlock(pluResult, &luTemp);
}
void InitSecurity(void)
{
NTSTATUS st;
/* We use the object manager to store the user list, so we need to create
* a directory object to contain the list */
st = NtCreateDirectoryObject(&hUserList, NULL);
if(!NT_SUCCESS(st))
hUserList = NULL;
InitializeCriticalSection(&csUserList);
}
PDMUSER PusrLookupUser(LPCSTR szUserName)
{
PDMUSER pusr;
OBJECT_ATTRIBUTES oa;
NTSTATUS st;
OBJECT_STRING ost;
HANDLE h;
/* First look up the handle -- we need to do this because this object
* isn't rooted in the object namespace. We don't synchronize access
* to the list since the object manager does that for us */
RtlInitObjectString(&ost, szUserName);
InitializeObjectAttributes(&oa, &ost, 0, hUserList, NULL);
st = ObOpenObjectByName(&oa, &obtUser, NULL, &h);
if(!NT_SUCCESS(st))
return NULL;
/* Now get the underlying object */
st = ObReferenceObjectByHandle(h, NULL, &pusr);
NtClose(h);
return NT_SUCCESS(st) ? pusr : NULL;
}
DWORD DmplAuthenticateUser(LPCSTR szUserName, PULARGE_INTEGER pluNonce,
PULARGE_INTEGER pluResponse, BOOL *pfKeyXchg)
{
PDMUSER pusr;
ULARGE_INTEGER luHandshake;
ULONG dmpl;
if(!szUserName) {
/* We're doing an admin password check */
if(*pfKeyXchg || !g_fAdminPasswd)
return 0;
XBCCross(&g_luAdminPasswd, pluNonce, &luHandshake);
return luHandshake.QuadPart == pluResponse->QuadPart ? -1 : 0;
}
/* Look up this user */
pusr = PusrLookupUser(szUserName);
if(!pusr)
return 0;
/* Verify the password */
if(pusr->fHasPasswd) {
if(!*pfKeyXchg) {
XBCCross(&pusr->luPasswd, pluNonce, &luHandshake);
dmpl = luHandshake.QuadPart == pluResponse->QuadPart ?
pusr->dwPrivileges : 0;
} else
dmpl = 0;
*pfKeyXchg = FALSE;
} else {
/* The user hasn't set a password yet. If we're getting the password
* now, then all is well. If not, we'll deny access and demand a
* password */
if(*pfKeyXchg) {
pusr->luPasswd.QuadPart = pluResponse->QuadPart;
pusr->fHasPasswd = TRUE;
dmpl = pusr->dwPrivileges;
*pfKeyXchg = FALSE;
/* We've changed the userlist, so we need to rewrite it */
WriteIniFile();
} else {
dmpl = 0;
*pfKeyXchg = TRUE;
}
}
/* Clean up */
ObDereferenceObject(pusr);
return dmpl;
}
HRESULT DmEnableSecurity(BOOL fEnable)
{
g_fLockLevel = !!fEnable;
RemoveAllUsers();
return XBDM_NOERR;
}
HRESULT DmIsSecurityEnabled(LPBOOL pfEnabled)
{
if(!pfEnabled)
return E_INVALIDARG;
*pfEnabled = g_fLockLevel != 0;
return XBDM_NOERR;
}
HRESULT HrDoAddUser(LPCSTR szUserName, PDMUSER pusrTemplate)
{
PDMUSER pusr;
HRESULT hr;
OBJECT_STRING ost;
OBJECT_ATTRIBUTES oa;
NTSTATUS st;
HANDLE h;
/* No users allowed if the box isn't locked */
if(!g_fLockLevel)
return XBDM_NOTLOCKED;
/* Synchronize access */
EnterCriticalSection(&csUserList);
/* See if this user already exists */
pusr = PusrLookupUser(szUserName);
if(pusr) {
LeaveCriticalSection(&csUserList);
return XBDM_ALREADYEXISTS;
}
/* Make a new user */
RtlInitObjectString(&ost, szUserName);
InitializeObjectAttributes(&oa, &ost, OBJ_PERMANENT, hUserList, NULL);
st = ObCreateObject(&obtUser, &oa, sizeof *pusr, &pusr);
if(!NT_SUCCESS(st)) {
LeaveCriticalSection(&csUserList);
return E_OUTOFMEMORY;
}
/* Now add this user to the handle table so it can be found later. Mark a
* pointer bias of 1 to keep the object persistent */
st = ObInsertObject(pusr, &oa, 1, &h);
/* We no longer need exclusive access to the user list */
LeaveCriticalSection(&csUserList);
/* We don't need our base reference to this object anymore. If we added it
* to the handle table, the pointer will still be valid and if not, we want
* to kill off the reference anyway */
ObDereferenceObject(pusr);
if(NT_SUCCESS(st)) {
memcpy(pusr, pusrTemplate, sizeof *pusr);
NtClose(h);
hr = XBDM_NOERR;
} else
hr = E_OUTOFMEMORY;
return hr;
}
HRESULT DmAddUser(LPCSTR szUserName, DWORD dmpl)
{
DMUSER usr;
memset(&usr, 0, sizeof usr);
usr.dwPrivileges = dmpl;
return HrDoAddUser(szUserName, &usr);
}
HRESULT DmRemoveUser(LPCSTR szUserName)
{
PDMUSER pusr;
HRESULT hr;
/* Synchronize access */
EnterCriticalSection(&csUserList);
/* Find the user we want to delete */
pusr = PusrLookupUser(szUserName);
/* Delete it if we found it */
if(pusr) {
/* Mark the object as temporary so it will be removed from the
* directory */
ObMakeTemporaryObject(pusr);
/* Deref the object twice -- once for our reference and once for the
* base reference -- to actually delete the object */
ObDereferenceObject(pusr);
ObDereferenceObject(pusr);
hr = XBDM_NOERR;
} else
hr = XBDM_NOSUCHFILE;
LeaveCriticalSection(&csUserList);
return hr;
}
HRESULT HrAddUserCommand(LPCSTR sz, LPSTR szResp, DWORD cchResp,
PDM_CMDCONT pdmcc)
{
DMUSER usr;
char szUserName[64];
HRESULT hr;
BOOL fRemove;
/* Get the user name */
if(!FGetSzParam(sz, "name", szUserName, sizeof szUserName)) {
strcpy(szResp, "missing name");
return E_FAIL;
}
/* If we're removing, then remove */
if(PchGetParam(sz, "remove", FALSE))
return DmRemoveUser(szUserName);
/* Set the password, if available. We only accept the password if this
* command is coming from the ini file */
if(!pdmcc && FGetQwordParam(sz, "passwd", &usr.luPasswd))
usr.fHasPasswd = TRUE;
else
usr.fHasPasswd = FALSE;
/* Set the privileges */
usr.dwPrivileges = DmplFromSz(sz);
/* Do the work */
return HrDoAddUser(szUserName, &usr);
}
void FillAccessPrivSz(LPSTR sz, DWORD dwPriv)
{
LPSTR szOrig = sz;
if(dwPriv & DMPL_PRIV_READ) {
strcpy(sz, " read");
sz += 5;
}
if(dwPriv & DMPL_PRIV_WRITE) {
strcpy(sz, " write");
sz += 6;
}
if(dwPriv & DMPL_PRIV_CONTROL) {
strcpy(sz, " control");
sz += 8;
}
if(dwPriv & DMPL_PRIV_CONFIGURE) {
strcpy(sz, " config");
sz += 7;
}
if(dwPriv & DMPL_PRIV_MANAGE) {
strcpy(sz, " manage");
sz += 7;
}
*sz = 0;
}
DWORD DmplFromSz(LPCSTR sz)
{
ULONG dmpl = 0;
if(PchGetParam(sz, "read", FALSE))
dmpl |= DMPL_PRIV_READ;
if(PchGetParam(sz, "write", FALSE))
dmpl |= DMPL_PRIV_WRITE;
if(PchGetParam(sz, "control", FALSE))
dmpl |= DMPL_PRIV_CONTROL;
if(PchGetParam(sz, "config", FALSE))
dmpl |= DMPL_PRIV_CONFIGURE;
if(PchGetParam(sz, "manage", FALSE))
dmpl |= DMPL_PRIV_MANAGE;
return dmpl;
}
void FillUserInfoSz(LPSTR sz, LPCSTR szName, DWORD dwPrivileges,
PULARGE_INTEGER pluPasswd)
{
char szPasswd[32];
if(pluPasswd)
sprintf(szPasswd, " passwd=0q%08x%08x", pluPasswd->HighPart,
pluPasswd->LowPart);
else
szPasswd[0] = 0;
sprintf(sz, "name=\"%s\"%s", szName, szPasswd);
FillAccessPrivSz(sz + strlen(sz), dwPrivileges);
}
void WriteIniSecurity(INF *pinf)
{
struct {
OBJECT_DIRECTORY_INFORMATION odi;
OCHAR sz[64];
} odi;
HRESULT hr;
HANDLE h;
int i;
PDMUSER pusr;
char sz[256];
EnterCriticalSection(&csUserList);
if(g_fLockLevel) {
sprintf(sz, "lockmode boxid=0q%08x%08x%s", g_luBoxId.HighPart,
g_luBoxId.LowPart, g_fLockLevel==2 ? " encrypt" : "");
WriteIniSz(pinf, sz);
}
if(g_fAdminPasswd) {
sprintf(sz, "adminpw passwd=0q%08x%08x", g_luAdminPasswd.HighPart,
g_luAdminPasswd.LowPart);
WriteIniSz(pinf, sz);
}
if(g_fLockLevel && hUserList) {
strcpy(sz, "user ");
i = 0;
while(NT_SUCCESS(NtQueryDirectoryObject(hUserList, &odi, sizeof odi,
i == 0, &i, NULL)))
{
ASSERT(odi.odi.Name.Buffer == odi.sz);
odi.sz[odi.odi.Name.Length] = 0;
pusr = PusrLookupUser(odi.odi.Name.Buffer);
if(pusr) {
FillUserInfoSz(sz + 5, odi.sz, pusr->dwPrivileges,
pusr->fHasPasswd ? &pusr->luPasswd : NULL);
WriteIniSz(pinf, sz);
ObDereferenceObject(pusr);
}
}
}
LeaveCriticalSection(&csUserList);
}
void RemoveAllUsers(void)
{
struct {
OBJECT_DIRECTORY_INFORMATION odi;
OCHAR sz[64];
} odi;
HRESULT hr;
HANDLE h;
int i;
PDMUSER pusr;
EnterCriticalSection(&csUserList);
/* We actually only empty the user list if the lock level is zero */
if(!g_fLockLevel && hUserList) {
i = 0;
while(NT_SUCCESS(NtQueryDirectoryObject(hUserList, &odi, sizeof odi,
i == 0, &i, NULL)))
{
ASSERT(odi.odi.Name.Buffer == odi.sz);
odi.sz[odi.odi.Name.Length] = 0;
pusr = PusrLookupUser(odi.odi.Name.Buffer);
if(pusr) {
ObMakeTemporaryObject(pusr);
ObDereferenceObject(pusr);
ObDereferenceObject(pusr);
i = 0;
}
}
}
LeaveCriticalSection(&csUserList);
}
DMHRAPI DmSetUserAccess(LPCSTR szUserName, DWORD dwAccess)
{
PDMUSER pusr;
pusr = PusrLookupUser(szUserName);
if(!pusr)
return XBDM_NOSUCHFILE;
pusr->dwPrivileges = dwAccess;
ObDereferenceObject(pusr);
return XBDM_NOERR;
}
DMHRAPI DmGetUserAccess(LPCSTR szUserName, LPDWORD lpdwAccess)
{
PDMUSER pusr;
pusr = PusrLookupUser(szUserName);
if(!pusr)
return XBDM_NOSUCHFILE;
*lpdwAccess = pusr->dwPrivileges;
ObDereferenceObject(pusr);
return XBDM_NOERR;
}
DMHRAPI DmWalkUserList(PDM_WALK_USERS *ppdmwu, PDM_USER pdusr)
{
struct {
OBJECT_DIRECTORY_INFORMATION odi;
OCHAR sz[64];
} odi;
HRESULT hr;
HANDLE h;
int i;
PDMUSER pusr;
NTSTATUS st;
if(!ppdmwu)
return E_INVALIDARG;
if(!g_fLockLevel || !hUserList)
return XBDM_ENDOFLIST;
st = NtQueryDirectoryObject(hUserList, &odi, sizeof odi,
*(PULONG)ppdmwu == 0, (PULONG)ppdmwu, NULL);
if(st == STATUS_NO_MORE_ENTRIES)
return XBDM_ENDOFLIST;
else if(!NT_SUCCESS(st))
return HrFromStatus(st, E_FAIL);
memset(pdusr, 0, sizeof *pdusr);
memcpy(pdusr->UserName, odi.odi.Name.Buffer, odi.odi.Name.Length);
pusr = PusrLookupUser(pdusr->UserName);
if(!pusr)
return E_UNEXPECTED;
pdusr->AccessPrivileges = pusr->dwPrivileges;
return XBDM_NOERR;
}
DMHRAPI DmCloseUserList(PDM_WALK_USERS pdmwu)
{
return XBDM_NOERR;
}