/* * * 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; }