538 lines
14 KiB
C
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;
|
|
}
|