//Copyright (c) 1998 - 1999 Microsoft Corporation /*++ Copyright (c) 1998 Citrix Systems Module Name: regfix.c Abstract: Reads hv (low) level structures in a hive and rewrites the hive structs after applying ACL fixes. Reads in the hive in block sizes. Scans for and processes security keys. Extracts the SECURITY_DESCRIPTOR structure form a cell and checks consistency of sizes of ACEs and ACLs. Usage: regfix in_filename out_filename Author: Maris Kurens (v-marisk (MS), marisk (CTXS)) Apr 1998 Revision History: Created - Apr 23, 1998 --*/ /* NOTE: This hive/registry tool does not read the entire hive into memory, but will read the hive file on a block by block basis, process each block and write out the block to a new file using file I/O. */ // Include NT headers #include #include #include #include #include "cmp.h" #include "regfix.h" #include #include #include #include #define INFILE 0x01 #define OUTFILE 0x02 #define BOTHFILE 0x03 int __cdecl main (int argc, char *argv[]); void openFile (void); void closeFile (unsigned which); void ScanHive (void); void DoKeySD (IN PCM_KEY_SECURITY Security, IN ULONG CellSize); void ScanCell (PHCELL Cell, ULONG CellSize); void DumpSecurityDescriptor (PSECURITY_DESCRIPTOR pSD); void CtxDumpSid (PSID, PCHAR, PULONG); void DumpAcl (PACL, PCHAR, PULONG); void DumpAce (PACE_HEADER, PCHAR, PULONG); BOOL AreWeRunningTerminalServices(void); VOID WINAPI ErrorPrintf (int nErrorResourceID, ...); LPCTSTR inFileName = NULL; LPCTSTR outFileName = NULL; HANDLE infilehandle; HANDLE outfilehandle; ULONG HiveVersion; // // SUMMARY TOTALS // ULONG SizeSDData=0; ULONG NumSDData=0; ULONG BlockNumb = 0; ULONG BadACL = 0; ULONG BadACE = 0; //--------------------------------------------------------- // Description : // Basic arg check, arg echo, process calls and result dump // // Args : // if you have to ask ... // // Return : // //--------------------------------------------------------- int __cdecl main (int argc, char *argv[]) { //Check if we are running under Terminal Server if(!AreWeRunningTerminalServices()) { ErrorPrintf(IDS_ERROR_NOT_TS); return(1); } if (argc < 3) { ErrorPrintf (IDS_ERROR_USAGE); exit (1); } inFileName = argv [1]; outFileName = argv [2]; // // echo the args // ErrorPrintf (IDS_WORKING); // // open the in and out files // openFile (); // // and process it // ScanHive (); ErrorPrintf (IDS_DONE); ErrorPrintf (IDS_SD_NUMBER, NumSDData); ErrorPrintf (IDS_BAD_ACL_NUMBER, BadACL); ErrorPrintf (IDS_BAD_ACE_NUMBER, BadACE); return (0); } //--------------------------------------------------------- // Description : // Closes file handles based on input args // // Args : // which - signals if input or output or both files should be closed // // Return : Nothing // //--------------------------------------------------------- void closeFile (unsigned which) { if (which & INFILE) { if (infilehandle != INVALID_HANDLE_VALUE) { CloseHandle (infilehandle); infilehandle = INVALID_HANDLE_VALUE; } } if (which & OUTFILE) { if (infilehandle != INVALID_HANDLE_VALUE) { CloseHandle (outfilehandle); outfilehandle = INVALID_HANDLE_VALUE; } } } //--------------------------------------------------------- // Description : // Opens input and output files // // Args : Nada // // Return : Nada // //--------------------------------------------------------- void openFile (void) { // // open the input file // infilehandle = CreateFile (inFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (infilehandle == INVALID_HANDLE_VALUE) { fprintf (stderr, "regfix: Could not open file '%s' error = %08lx\n", inFileName, GetLastError()); exit(1); } // // open the output file // outfilehandle = CreateFile (outFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (outfilehandle == INVALID_HANDLE_VALUE) { closeFile (INFILE); fprintf (stderr, "hivestat: Could not create file '%s' error = %08lx\n", outFileName, GetLastError()); exit(1); } } //--------------------------------------------------------- // Description : // Scan the hive, looking for security cells. Load the // hive block by block and write each block to the output // file after processing it. // // Args : None // // Return : Nyet // //--------------------------------------------------------- void ScanHive (void) { static char buffer[HBLOCK_SIZE]; PHBASE_BLOCK bbp; BOOL rf; BOOL wf; ULONG readcount; ULONG writecount; ULONG hivelength; ULONG hiveposition; PHCELL cp; PHCELL guard; PHBIN hbp; ULONG hoff; ULONG binread; ULONG binsize; ULONG cellsize; ULONG boff; ULONG lboff; ULONG SizeTotal; // // read the header // rf = ReadFile (infilehandle, buffer, HBLOCK_SIZE, &readcount, NULL); if ((!rf) || (readcount != HBLOCK_SIZE) ) { closeFile (BOTHFILE); fprintf (stderr, "regfix: '%s' - cannot read base block!\n", inFileName); exit(1); } BlockNumb++; bbp = (PHBASE_BLOCK)(&(buffer[0])); if ((bbp->Major != HSYS_MAJOR) || (bbp->Minor > HSYS_MINOR)) { closeFile (BOTHFILE); fprintf(stderr, "hivestat: major/minor != %d/%d get newer hivestat\n", HSYS_MAJOR, HSYS_MINOR ); exit(1); } HiveVersion = bbp->Minor; hivelength = bbp->Length + HBLOCK_SIZE; hiveposition = HBLOCK_SIZE; hoff = 0; // // scan the hive // guard = (PHCELL)(&(buffer[0]) + HBLOCK_SIZE); wf = WriteFile (outfilehandle, buffer, HBLOCK_SIZE, &writecount, NULL); if ((!wf) || (writecount != HBLOCK_SIZE) ) { closeFile (BOTHFILE); fprintf (stderr, "regfix: '%s' - cannot write base block!\n", outFileName); exit(1); } // // hiveposition is file relative offset of next block we will read // // hoff is the file relative offset of the last block we read // // hivelength is actual length of file (header's recorded length plus // the size of the header. // // cp is pointer into memory, within range of buffer, it's a cell pointer // while (hiveposition < hivelength) { // // read in first block of bin, check signature, determine key type // rf = ReadFile (infilehandle, buffer, HBLOCK_SIZE, &readcount, NULL); if ((! rf) || (readcount != HBLOCK_SIZE)) { closeFile (BOTHFILE); fprintf (stderr, "hivestat: '%s' read error @%08lx\n", inFileName, hiveposition); exit (1); } BlockNumb++; hbp = (PHBIN)(&(buffer[0])); if (hbp->Signature != HBIN_SIGNATURE) { closeFile (BOTHFILE); fprintf(stderr, "hivestat: '%s' bad bin sign. @%08lx\n", inFileName, hiveposition); exit(1); } hiveposition += HBLOCK_SIZE; hoff += HBLOCK_SIZE; ASSERT (hoff+HBLOCK_SIZE == hiveposition); binsize = hbp->Size; // // scan the bin // // cp = pointer to cell we are looking at // boff = offset within bin // lboff = last offset within bin, used only for consistency checks // binread = number of bytes of bin we've read so far // cp = (PHCELL)((PUCHAR)hbp + sizeof(HBIN)); boff = sizeof(HBIN); lboff = (ULONG)-1; binread = HBLOCK_SIZE; while (binread <= binsize) { // // if free, update pointer only // else scan it // if (cp->Size > 0) { cellsize = cp->Size; } else { cellsize = -1 * cp->Size; ScanCell (cp, cellsize); } // // do basic consistency check // #if 0 if (cp->Last != lboff) { printf("e!,x%08lx bad LAST pointer %08lx\n", hoff+((PUCHAR)cp - &(buffer[0])), cp->Last); } #endif // // advance to next cell // lboff = boff; cp = (PHCELL)((PUCHAR)cp + cellsize); boff += cellsize; // // scan ahead in bin, if cp has reached off end of block, // AND there's bin left to read. // do this BEFORE breaking out for boff at end. // while ((cp >= guard) && (binread < binsize)) { // write out the currently loaded block wf = WriteFile (outfilehandle, buffer, HBLOCK_SIZE, &writecount, NULL); if ((!wf) || (writecount != HBLOCK_SIZE) ) { closeFile (BOTHFILE); fprintf (stderr, "regfix: '%s' - cannot write block %d!\n", BlockNumb, outFileName); exit(1); } rf = ReadFile(infilehandle, buffer, HBLOCK_SIZE, &readcount, NULL); if ((!rf) || (readcount != HBLOCK_SIZE)) { closeFile (BOTHFILE); fprintf(stderr, "hivestat: '%s' read error @%08lx\n", inFileName, hiveposition); exit(1); } BlockNumb++; cp = (PHCELL)((PUCHAR)cp - HBLOCK_SIZE); hiveposition += HBLOCK_SIZE; hoff += HBLOCK_SIZE; binread += HBLOCK_SIZE; ASSERT (hoff+HBLOCK_SIZE == hiveposition); } if (boff >= binsize) { break; // we are done with this bin } } wf = WriteFile (outfilehandle, buffer, HBLOCK_SIZE, &writecount, NULL); if ((!wf) || (writecount != HBLOCK_SIZE) ) { closeFile (BOTHFILE); fprintf (stderr, "regfix: '%s' - cannot write block %d!\n", BlockNumb, outFileName); exit(1); } } return; } //--------------------------------------------------------- // Description : // Given a pointer to an HCELL, check the SD type cell signature // If it is pass to the SD processing routines. // Note : framework is in place to handle other cell types // // Args : // Cell - Supplies a pointer to the HCELL // // CellSize - Supplies the size of the HCELL // // Return : Nothing // //--------------------------------------------------------- void ScanCell (IN PHCELL Cell, IN ULONG CellSize) { PCELL_DATA Data; if (HiveVersion==1) { Data = (PCELL_DATA)&Cell->u.OldCell.u.UserData; } else { Data = (PCELL_DATA)&Cell->u.NewCell.u.UserData; } // // grovel through the data, see if we can find the SD keys // if ((Data->u.KeyNode.Signature == CM_KEY_NODE_SIGNATURE) && (CellSize > sizeof(CM_KEY_NODE))) { // // probably a key node // return; } else if ((Data->u.KeyValue.Signature == CM_KEY_VALUE_SIGNATURE) && (CellSize > sizeof(CM_KEY_VALUE))) { // // probably a key value // return; } else if ((Data->u.KeySecurity.Signature == CM_KEY_SECURITY_SIGNATURE) && (CellSize > sizeof(CM_KEY_SECURITY))) { // // probably a security descriptor // DoKeySD (&Data->u.KeySecurity, CellSize); } else if ((Data->u.KeyIndex.Signature == CM_KEY_INDEX_ROOT) || (Data->u.KeyIndex.Signature == CM_KEY_INDEX_LEAF)) { // // probably a key index // return; } else { // // Nothing with a signature, could be either // name // key list // value data // return; } } //--------------------------------------------------------- // Description : // Expects an SD cell pointer. Extracts the SD descriptor // and passes it on for further processing // // Args : // Security - Pointer to PCM_KEY_SECURITY type cell // // CellSize - size of the HCELL // // Return : Nothing // //--------------------------------------------------------- void DoKeySD (IN PCM_KEY_SECURITY Security, IN ULONG CellSize) { PSECURITY_DESCRIPTOR pSD; SizeSDData += CellSize; NumSDData++; pSD = &Security->Descriptor; DumpSecurityDescriptor (pSD); } //--------------------------------------------------------- // ** The following routines are hacks of the dumpsd // ** lib code in private\citrix\syslib // ** This util can easily be modified as a standalone // ** utility for a variety of hive security processing //--------------------------------------------------------- //--------------------------------------------------------- // Description : // Unfolds an SD descriptor passing SID and ACL pointers // for further processing // // Args : // pSD - Pointer to SECURITY_DESCRIPTOR structure // // Return : Nothing // //--------------------------------------------------------- void DumpSecurityDescriptor (PSECURITY_DESCRIPTOR pSD) { PISECURITY_DESCRIPTOR p = (PISECURITY_DESCRIPTOR)pSD; PSID pSid; PACL pAcl; PCHAR pTmp; ULONG Size; /* DbgPrint ("DUMP_SECURITY_DESCRIPTOR: Revision %d, Sbz1 %d, Control 0x%x\n", p->Revision, p->Sbz1, p->Control ); if (p->Control & SE_SELF_RELATIVE ) { DbgPrint("Self Relative\n"); } DbgPrint("PSID Owner 0x%x\n",p->Owner); */ // If this is self relative, must offset the pointers if( p->Owner != NULL ) { if (p->Control & SE_SELF_RELATIVE) { pTmp = (PCHAR)pSD; pTmp += (ULONG)p->Owner; CtxDumpSid ((PSID)pTmp, (PCHAR)p, &Size ); } else { // can reference it directly CtxDumpSid (p->Owner, (PCHAR)p, &Size ); } } /* DbgPrint("PSID Group 0x%x\n",p->Group); */ // If this is self relative, must offset the pointers if (p->Group != NULL) { if (p->Control & SE_SELF_RELATIVE) { pTmp = (PCHAR)pSD; pTmp += (ULONG)p->Group; CtxDumpSid( (PSID)pTmp, (PCHAR)p, &Size ); } else { // can reference it directly CtxDumpSid( p->Group, (PCHAR)p, &Size ); } } // DbgPrint("\n"); // DbgPrint("PACL Sacl 0x%x\n",p->Sacl); // If this is self relative, must offset the pointers if (p->Sacl != NULL) { if (p->Control & SE_SELF_RELATIVE) { pTmp = (PCHAR)pSD; pTmp += (ULONG)p->Sacl; DumpAcl( (PSID)pTmp, (PCHAR)p, &Size ); } else { // can reference it directly DumpAcl (p->Sacl, (PCHAR)p, &Size); } } // DbgPrint("\n"); // DbgPrint ("PACL Dacl 0x%x\n",p->Dacl); // If this is self relative, must offset the pointers if (p->Dacl != NULL) { if (p->Control & SE_SELF_RELATIVE) { pTmp = (PCHAR)pSD; pTmp += (ULONG)p->Dacl; DumpAcl( (PSID)pTmp, (PCHAR)p, &Size ); } else { // can reference it directly DumpAcl( p->Dacl, (PCHAR)p, &Size ); } } } //--------------------------------------------------------- // Description : // Examine an SD descriptor for subauthority and owner // info // // Args : // pSid - Pointer to SID structure // pBase - not used (kept for historical reasons) // pSize - holds the SID size on return // // Return : Nothing // //--------------------------------------------------------- void CtxDumpSid ( PSID pSid, PCHAR pBase, PULONG pSize) { PISID p; ULONG i; BOOL OK; DWORD szUserName; DWORD szDomain; SID_NAME_USE UserSidType; WCHAR UserName[256]; WCHAR Domain[256]; ULONG Size = 0; p = (PISID)pSid; // DbgPrint("Revision %d, SubAuthorityCount %d\n", p->Revision, p->SubAuthorityCount); Size += 2; // Revision, SubAuthorityCount /* DbgPrint("IdentifierAuthority: %x %x %x %x %x %x\n", p->IdentifierAuthority.Value[0], p->IdentifierAuthority.Value[1], p->IdentifierAuthority.Value[2], p->IdentifierAuthority.Value[3], p->IdentifierAuthority.Value[4], p->IdentifierAuthority.Value[5] ); */ Size += 6; // IdentifierAuthority for (i=0; i < p->SubAuthorityCount; i++) { // DbgPrint("SubAuthority[%d] 0x%x\n", i, p->SubAuthority[i]); Size += sizeof(ULONG); } if (pSize) { *pSize = Size; } szUserName = sizeof (UserName); szDomain = sizeof (Domain); // Now print its account /* OK = LookupAccountSidW ( NULL, // Computer Name pSid, UserName, &szUserName, Domain, &szDomain, &UserSidType); */ // if (OK) // { // DbgPrint("Account Name %ws, Domain %ws, Type %d, SidSize %d\n",UserName,Domain,UserSidType,Size); // } // else // { // DbgPrint("Error looking up account name %d, SizeSid %d\n",GetLastError(),Size); // } } //--------------------------------------------------------- // Description : // Unfolds an ACL dumping all the ACEs in the process // checking consistency by tracking the actual size // // Args : // pAcl - Pointer to ACL structure // pBase - not used (kept for historical reasons) // pSize - holds the size on return // // Return : Nothing // //--------------------------------------------------------- void DumpAcl (PACL pAcl, PCHAR pBase, PULONG pSize) { USHORT i; PCHAR pTmp; ULONG Size, MySize; PACL p = pAcl; PCHAR pCur = (PCHAR)pAcl; MySize = 0; // DbgPrint ("AclRevision %d, Sbz1 %d, AclSize %d, AceCount %d, Sbz2 %d\n", // p->AclRevision, p->Sbz1, p->AclSize, p->AceCount, p->Sbz2); // bump over the ACL header to point to the first ACE pCur += sizeof (ACL); MySize += sizeof (ACL); for (i=0; i < p->AceCount; i++) { DumpAce ((PACE_HEADER)pCur, pBase, &Size ); pCur += Size; MySize += Size; } // ACL consistency check if( p->AclSize != MySize ) { // // HACK!HACK!HACK! // | // | // | // v if (p->AclSize == 1023) // This hack is in response to MS bug #1607. The hive load fails on ACL { // size alignment check. The sw hive file that causes this problem has p->AclSize = 1024; // about 3k SD entries of ACL size 1023. This hack adjusts this. } // ^ // | // | // | // | // HACK!HACK!HACK! // DbgPrint("Inconsistent ACL Entry! p->AclSize %d, RealSize %d\n",p->AclSize,MySize); // p->AclSize = MySize; BadACL++; } // return the size of this ACL *pSize = MySize; return; } //--------------------------------------------------------- // Description : // Unfolds an ACE descriptor checking consistency by // tracking the actual size // // Args : // pAce - Pointer to ACE structure // pBase - not used (kept for historical reasons) // pSize - holds the size on return // // Return : Nothing // //--------------------------------------------------------- void DumpAce (PACE_HEADER pAce, PCHAR pBase, PULONG pSize) { PACE_HEADER p = pAce; PACCESS_ALLOWED_ACE pAl; PACCESS_DENIED_ACE pAd; PSYSTEM_AUDIT_ACE pSa; PSYSTEM_ALARM_ACE pSl; PCHAR pTmp; ULONG MySize, Size, saveSize; // DbgPrint ("ACE_HEADER: Type %d, Flags 0x%x, Size %d\n", // p->AceType, p->AceFlags, p->AceSize ); switch (p->AceType) { case ACCESS_ALLOWED_ACE_TYPE: pAl = (PACCESS_ALLOWED_ACE)p; // DbgPrint("ACCESS_ALLOWED_ACE: AccessMask 0x%x, Sid 0x%x\n",pAl->Mask,pAl->SidStart); MySize = sizeof(ACCESS_ALLOWED_ACE); if (pAl->SidStart) { pTmp = (PCHAR)&pAl->SidStart; CtxDumpSid( (PSID)pTmp, pBase, &Size ); MySize += Size; // Adjust for the first ULONG of the ACE // being part of the Sid MySize -= sizeof(ULONG); } break; case ACCESS_DENIED_ACE_TYPE: pAd = (PACCESS_DENIED_ACE)p; // DbgPrint("ACCESS_DENIED_ACE: AccessMask 0x%x, Sid 0x%x\n",pAd->Mask,pAd->SidStart); MySize = sizeof(ACCESS_DENIED_ACE); if (pAd->SidStart) { pTmp = (PCHAR)&pAd->SidStart; CtxDumpSid( (PSID)pTmp, pBase, &Size ); MySize += Size; // Adjust for the first ULONG of the ACE // being part of the Sid MySize -= sizeof(ULONG); } break; case SYSTEM_AUDIT_ACE_TYPE: pSa = (PSYSTEM_AUDIT_ACE)p; // DbgPrint("SYSTEM_AUDIT_ACE: AccessMask 0x%x, Sid 0x%x\n",pSa->Mask,pSa->SidStart); MySize = sizeof(SYSTEM_AUDIT_ACE); if ( pSa->SidStart ) { pTmp = (PCHAR)&pSa->SidStart; CtxDumpSid( (PSID)pTmp, pBase, &Size ); MySize += Size; // Adjust for the first ULONG of the ACE // being part of the Sid MySize -= sizeof(ULONG); } break; case SYSTEM_ALARM_ACE_TYPE: pSl = (PSYSTEM_ALARM_ACE)p; // DbgPrint("SYSTEM_ALARM_ACE: AccessMask 0x%x, Sid 0x%x\n",pSl->Mask,pSl->SidStart); MySize = sizeof(SYSTEM_ALARM_ACE); if (pSl->SidStart) { pTmp = (PCHAR)&pSl->SidStart; CtxDumpSid( (PSID)pTmp, pBase, &Size ); MySize += Size; // Adjust for the first ULONG of the ACE // being part of the Sid MySize -= sizeof(ULONG); } break; default: break; // DbgPrint("Unknown ACE type %d\n", p->AceType); } saveSize = p->AceSize; // Check its consistency if ( p->AceSize != MySize ) { // DbgPrint("Inconsistent ACE Entry! p->AceSize %d, RealSize %d\n",p->AceSize,MySize); // p->AceSize = MySize; BadACE++; } // return the size so the caller can update the pointer // *pSize = p->AceSize; *pSize = saveSize; // DbgPrint("\n"); return; } /******************************************************************************* * * AreWeRunningTerminalServices * * Check if we are running terminal server * * ENTRY: * * EXIT: BOOL: True if we are running Terminal Services False if we * are not running Terminal Services * * ******************************************************************************/ BOOL AreWeRunningTerminalServices(void) { OSVERSIONINFOEX osVersionInfo; DWORDLONG dwlConditionMask = 0; ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX)); osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osVersionInfo.wSuiteMask = VER_SUITE_TERMINAL; VER_SET_CONDITION( dwlConditionMask, VER_SUITENAME, VER_AND ); return VerifyVersionInfo( &osVersionInfo, VER_SUITENAME, dwlConditionMask ); }