/*++ Copyright (c) 2000-2001 Microsoft Corporation Module Name: PrinterGetPrtL2.cpp Abstract: This is a shim that can be applied to those applications who assumed false upper-limit on the size of PRINTER_INFO_2 buffer GetPrinter returns. What the shim does is that when it detects the spooler returned PRINTER_INFO_2 buffer is over a certain limit that could break those apps, it truncates the private devmode part in PRINTER_INFO_2's pDevMode to retain only the public devmode, and returns the truncated PRINTER_INFO_2 buffer. History: 10/29/2001 fengy Created --*/ #include "precomp.h" #include IMPLEMENT_SHIM_BEGIN(PrinterGetPrtL2) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(GetPrinterA) APIHOOK_ENUM_END #define ALIGN_PTR_UP(addr) ((ULONG_PTR)((ULONG_PTR)(addr) + (sizeof(ULONG_PTR) - 1)) & ~(sizeof(ULONG_PTR) - 1)) // // The PRINTER_INFO_2 buffer size limit over which this shim // will perform the devmode truncation. The default value is // set to 8K here. App-specific limit values can be specified // as decimal numbers in the XML database as following: // // // LONG g_lTruncateLimit = 0x2000; // // Table of offsets to each string field in PRINTER_INFO_2A // DWORD g_dwStringOffsets[] = {offsetof(PRINTER_INFO_2A, pServerName), offsetof(PRINTER_INFO_2A, pPrinterName), offsetof(PRINTER_INFO_2A, pShareName), offsetof(PRINTER_INFO_2A, pPortName), offsetof(PRINTER_INFO_2A, pDriverName), offsetof(PRINTER_INFO_2A, pComment), offsetof(PRINTER_INFO_2A, pLocation), offsetof(PRINTER_INFO_2A, pSepFile), offsetof(PRINTER_INFO_2A, pPrintProcessor), offsetof(PRINTER_INFO_2A, pDatatype), offsetof(PRINTER_INFO_2A, pParameters), 0xFFFFFFFF}; /*++ This stub function intercepts all level 2 calls to GetPrinterA and detects if the spooler returned PRINTER_INFO_2 buffer is over the app limit. If so, it will truncate the private devmode of PRINTER_INFO_2's pDevMode. --*/ BOOL APIHOOK(GetPrinterA)( HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded ) { BOOL bNoTruncation = TRUE; BOOL bRet; if (Level == 2 && pPrinter != NULL && cbBuf != 0) { PRINTER_INFO_2A *pInfo2Full = NULL; DWORD cbFullNeeded = 0; // // Call spooler to get the full PRINTER_INFO_2 buffer size // bRet = ORIGINAL_API(GetPrinterA)( hPrinter, 2, NULL, 0, &cbFullNeeded); if (!bRet && GetLastError() == ERROR_INSUFFICIENT_BUFFER && cbFullNeeded > (DWORD)g_lTruncateLimit && cbBuf >= cbFullNeeded && (pInfo2Full = (PRINTER_INFO_2A *)malloc(cbFullNeeded))) { // // The full PRINTER_INFO_2 buffer size is over the app limit, // so we will first retrieve the full buffer into our internal // buffer. // // The condition "cbBuf >= cbFullNeeded" is here because if // that's not true, the original GetPrinterA's behavior is to // fail the call. We don't want to change that behavior. // bRet = ORIGINAL_API(GetPrinterA)( hPrinter, 2, (PBYTE)pInfo2Full, cbFullNeeded, &cbFullNeeded); if (bRet) { PRINTER_INFO_2A *pInfo2In; PBYTE pDest; INT i; // // Having the full PRINTER_INFO_2 structure in our internal // buffer, now we will copy it into app-allocated output buffer // by duplicating every thing except for the pDevMode. For // pDevMode we will only copy over the public devmode part, // because the bigger size of PRINTER_INFO_2 structure usually // are caused by big private devmode. // pInfo2In = (PRINTER_INFO_2A *)pPrinter; pDest = (PBYTE)pInfo2In + sizeof(PRINTER_INFO_2A); // // First copy over all the strings // i = 0; while (g_dwStringOffsets[i] != (-1)) { PSTR pSrc; pSrc = *(PSTR *)((PBYTE)pInfo2Full + g_dwStringOffsets[i]); if (pSrc) { DWORD cbStrSize; cbStrSize = strlen(pSrc) + sizeof(CHAR); memcpy(pDest, pSrc, cbStrSize); *(PSTR *)((PBYTE)pInfo2In + g_dwStringOffsets[i]) = (PSTR)pDest; pDest += cbStrSize; } else { *(PSTR *)((PBYTE)pInfo2In + g_dwStringOffsets[i]) = NULL; } i++; } // // Then truncate the private devmode part by only copying over // public devmode // if (pInfo2Full->pDevMode) { pDest = (PBYTE)ALIGN_PTR_UP(pDest); memcpy(pDest, pInfo2Full->pDevMode, pInfo2Full->pDevMode->dmSize); pInfo2In->pDevMode = (DEVMODEA *)pDest; // // Set private devmode size to zero since it was just truncated // pInfo2In->pDevMode->dmDriverExtra = 0; pDest += pInfo2In->pDevMode->dmSize; } else { pInfo2In->pDevMode = NULL; } // // Then copy over the security descriptor // if (pInfo2Full->pSecurityDescriptor && IsValidSecurityDescriptor(pInfo2Full->pSecurityDescriptor)) { DWORD cbSDSize; cbSDSize = GetSecurityDescriptorLength(pInfo2Full->pSecurityDescriptor); pDest = (PBYTE)ALIGN_PTR_UP(pDest); memcpy(pDest, pInfo2Full->pSecurityDescriptor, cbSDSize); pInfo2In->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)pDest; pDest += cbSDSize; } else { pInfo2In->pSecurityDescriptor = NULL; } // // Lastly copy over all the DWORD fields // pInfo2In->Attributes = pInfo2Full->Attributes; pInfo2In->Priority = pInfo2Full->Priority; pInfo2In->DefaultPriority = pInfo2Full->DefaultPriority; pInfo2In->StartTime = pInfo2Full->StartTime; pInfo2In->UntilTime = pInfo2Full->UntilTime; pInfo2In->Status = pInfo2Full->Status; pInfo2In->cJobs = pInfo2Full->cJobs; pInfo2In->AveragePPM = pInfo2Full->AveragePPM; // // We also need to set the correct return buffer size // if (pcbNeeded) { *pcbNeeded = pDest - pPrinter; } bNoTruncation = FALSE; DPFN(eDbgLevelInfo, "GetPrinterA truncated from %X to %X bytes", cbBuf, pDest - pPrinter); } } if (pInfo2Full) { free(pInfo2Full); pInfo2Full = NULL; } } if (bNoTruncation) { // // The shim doesn't need to do any truncation, or it has hit // an error when it was doing the truncation, so we will // just let the original API handle the call. // bRet = ORIGINAL_API(GetPrinterA)( hPrinter, Level, pPrinter, cbBuf, pcbNeeded); } return bRet; } /*++ Handle DLL_PROCESS_ATTACH to retrieve command line parameter. --*/ BOOL NOTIFY_FUNCTION( DWORD fdwReason ) { if (fdwReason == DLL_PROCESS_ATTACH) { LONG lLimit = atol(COMMAND_LINE); if (lLimit > 0) { g_lTruncateLimit = lLimit; } } return TRUE; } /*++ Register hooked functions --*/ HOOK_BEGIN CALL_NOTIFY_FUNCTION APIHOOK_ENTRY(WINSPOOL.DRV, GetPrinterA); HOOK_END IMPLEMENT_SHIM_END