static void PluginLoadGenericSingle( _Inout_ PPLUGIN_HEAD plugin ) { TCHAR path[MAX_PATH] = { 0 }; size_t size = 0; size = _stprintf_s(path, _countof(path), "%s/%s.%s", plugin->type->directory, PLUGIN_GET_NAME(plugin), PLUGINS_FILE_EXTENSION); if (size == -1) { FATAL(_T("Cannot construct path for module <%s> (directory is <%s>)"), PLUGIN_GET_NAME(plugin), plugin->type->directory); } plugin->module = LoadLibrary(path); if (!plugin->module) { FATAL(_T("Cannot open module for plugin <%s> : <%u> (path is <%s>)"), PLUGIN_GET_NAME(plugin), GetLastError(), path); } else { LOG(Dbg, _T("Loaded module for plugin <%s> : <%#08x>"), PLUGIN_GET_NAME(plugin), plugin->module); } PLUGIN_RESOLVE_FN(plugin, PLUGIN_PFN_INITIALIZE, Initialize, PLUGIN_GENERIC_INITIALIZE, FALSE); PLUGIN_RESOLVE_FN(plugin, PLUGIN_PFN_FINALIZE, Finalize, PLUGIN_GENERIC_FINALIZE, FALSE); PLUGIN_RESOLVE_FN(plugin, PLUGIN_PFN_SIMPLE, Help, PLUGIN_GENERIC_HELP, TRUE); #pragma warning(disable:4054) PLUGIN_RESOLVE_VAR(plugin, LPTSTR, description, PLUGIN_GENERIC_DESCRIPTION, FALSE); PLUGIN_RESOLVE_VAR(plugin, LPTSTR, keyword, PLUGIN_GENERIC_KEYWORD, TRUE); #pragma warning(default:4054) PluginLoadRequirements(plugin); }
BOOL PluginUnload( _Inout_ PPLUGIN_HEAD plugin ) { LOG(Info, SUB_LOG(_T("Unloading plugin <%s>")), PLUGIN_GET_NAME(plugin)); BOOL bResult = FreeLibrary(plugin->module); if (!bResult) { LOG(Warn, SUB_LOG(_T("Cannot close plugin <%s> : <%u>")), PLUGIN_GET_NAME(plugin), GetLastError()); } return bResult; }
static DWORD PluginLoadGenericMultiple( _Inout_ LPTSTR names, _Inout_ PBYTE pluginsArray, _In_ DWORD max, _In_ PLUGIN_TYPE const * const type ) { DWORD count = 0; DWORD i = 0; LPTSTR pluginName = NULL; LPTSTR ctx = NULL; PPLUGIN_HEAD plugin = NULL; while (StrNextToken(names, _T(","), &ctx, &pluginName)) { LOG(Info, SUB_LOG(_T("Loading %s <%s>")), type->name, pluginName); if (count >= max) { FATAL(_T("Cannot load more than <%u> %ss"), max, type->name); } for (i = 0; i < count; i++) { if (STR_EQ(pluginName, PLUGIN_GET_NAME((PPLUGIN_HEAD)(pluginsArray + (i * type->size))))) { FATAL(_T("%s <%s> is already loaded"), type->name, pluginName); } } plugin = (PPLUGIN_HEAD)(pluginsArray + (count * type->size)); PLUGIN_SET_NAME(plugin, pluginName); PLUGIN_SET_TYPE(plugin, type); PluginLoadGenericSingle(plugin); count++; } return count; }
BOOL PluginFinalize( _Inout_ PPLUGIN_HEAD plugin ) { LOG(Info, SUB_LOG(_T("Finalizing plugin <%s>")), PLUGIN_GET_NAME(plugin)); if (plugin->functions.Finalize) { BOOL bResult = plugin->functions.Finalize(&gc_PluginApiTable); if (!bResult){ LOG(Err, SUB_LOG(_T("Cannot finalize plugin <%s>")), PLUGIN_GET_NAME(plugin)); } return bResult; } LOG(Dbg, SUB_LOG(_T("Plugin <%s> does not have an finalizer")), PLUGIN_GET_NAME(plugin)); return TRUE; }
BOOL PluginHelp( _Inout_ PPLUGIN_HEAD plugin ) { LOG(Bypass, _T("------------------------------")); LOG(Bypass, _T("Help for %s <%s> :"), plugin->type->name, PLUGIN_GET_NAME(plugin)); LOG(Bypass, SUB_LOG(_T("Description : %s")), PLUGIN_GET_DESCRIPTION(plugin)); plugin->functions.Help(&gc_PluginApiTable); LOG(Bypass, EMPTY_STR); return TRUE; }
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ static FARPROC PluginResolve( _Inout_ PPLUGIN_HEAD plugin, _In_ LPCSTR procName, _In_ BOOL fatal ) { FARPROC proc = GetProcAddress(plugin->module, procName); if (!proc && fatal) { FATAL(_T("Failed to resolve required symbol <%s> for plugin <%s>"), procName, PLUGIN_GET_NAME(plugin)); } else { LOG(Dbg, _T("Symbol <%s> for plugin <%s> : <%p>"), procName, PLUGIN_GET_NAME(plugin), proc); } return proc; }
static void ParseOptions( _In_ PACE_FILTER_OPTIONS opt, _In_ int argc, _In_ TCHAR * argv[] ) { const static struct option long_options[] = { { _T("list-plugins"), no_argument, 0, _T('L') }, { _T("list-importers"), no_argument, 0, _T('I') }, { _T("list-filters"), no_argument, 0, _T('F') }, { _T("list-writers"), no_argument, 0, _T('W') }, { _T("importers"), required_argument, 0, _T('i') }, { _T("filters"), required_argument, 0, _T('f') }, { _T("writers"), required_argument, 0, _T('w') }, { _T("invert-filters"), required_argument, 0, _T('r') }, { _T("progression"), required_argument, 0, _T('p') }, { _T("logfile"), required_argument, 0, _T('l') }, { _T("dbglvl"), required_argument, 0, _T('d') }, { _T("loglvl"), required_argument, 0, _T('d') }, { _T("help"), no_argument, 0, _T('h') }, { _T("usage"), no_argument, 0, _T('h') }, { 0, 0, 0, 0 } }; int curropt = 0; BOOL bExitAfterOpt = FALSE; LPTSTR filter = NULL; LPTSTR ctx = NULL; // // Default options // SetLogLevel(DEFAULT_OPT_DEBUGLEVEL); opt->misc.progression = DEFAULT_OPT_PROGRESSION; // // Parsing // while ((curropt = getopt_long_only(argc, argv, EMPTY_STR, long_options, NULL)) != -1) { switch (curropt) { // // List // case _T('L'): SetLogLevel(_T("NONE")); ListImporters(); ListFilters(); ListWriters(); ExitProcess(EXIT_SUCCESS); break; case _T('I'): SetLogLevel(_T("NONE")); ListImporters(); bExitAfterOpt = TRUE; break; case _T('F'): SetLogLevel(_T("NONE")); ListFilters(); bExitAfterOpt = TRUE; break; case _T('W'): SetLogLevel(_T("NONE")); ListWriters(); bExitAfterOpt = TRUE; break; // // Plugins // case _T('i'): opt->names.importers = optarg; opt->plugins.numberOfImporters = PluginLoadImporters(opt->names.importers, opt->plugins.importers, PLUGIN_MAX_IMPORTERS); break; case _T('f'): opt->names.filters = optarg; opt->plugins.numberOfFilters = PluginLoadFilters(opt->names.filters, opt->plugins.filters, PLUGIN_MAX_FILTERS); break; case _T('w'): opt->names.writers = optarg; opt->plugins.numberOfWriters = PluginLoadWriters(opt->names.writers, opt->plugins.writers, PLUGIN_MAX_WRITERS); break; case _T('r'): opt->names.inverted = optarg; DWORD i = 0; filter = NULL; ctx = NULL; // TODO : invert after filters (numOfF == 0) while (StrNextToken(opt->names.inverted, ",", &ctx, &filter)) { for (i = 0; i < PLUGIN_MAX_FILTERS; i++) { if (PLUGIN_IS_LOADED(&opt->plugins.filters[i])) { if (STR_EQ(filter, PLUGIN_GET_NAME(&opt->plugins.filters[i]))) { LOG(Info, SUB_LOG(_T("Inverting filter <%s>")), filter); opt->plugins.filters[i].inverted = TRUE; break; } } } if (i == PLUGIN_MAX_FILTERS) { FATAL(_T("Trying to invert a non-loaded filter : <%s>"), filter); } } break; // // Misc // case _T('p'): opt->misc.progression = _tstoi(optarg); LOG(Info, SUB_LOG(_T("Printing progression every <%u> ACE")), opt->misc.progression); break; case _T('l'): opt->misc.logfile = optarg; SetLogFile(opt->misc.logfile); break; case _T('d'): opt->misc.loglvl = optarg; SetLogLevel(opt->misc.loglvl); break; case _T('h'): opt->misc.showHelp = TRUE; break; default: FATAL(_T("Unknown option")); break; } } LPTSTR optname = NULL; LPTSTR optval = NULL; int i = 0; for (i = optind; i < argc; i++) { ctx = NULL; StrNextToken(argv[i], "=", &ctx, &optname); optval = ctx; //We do not use StrNextToken here because optval can contain an equal sign. if (!optname || !optval) { FATAL(_T("Cannot parse plugin option <%s> (must be in format name=value)"), argv[i]); } else { LOG(Dbg, _T("Adding plugin option <%s : %s>"), optname, optval); AddStrPair(&gs_PluginOptions.end, optname, optval); if (!gs_PluginOptions.head) { gs_PluginOptions.head = gs_PluginOptions.end; } } } if (bExitAfterOpt) { ExitProcess(EXIT_SUCCESS); } }
int main( _In_ int argc, _In_ TCHAR * argv[] ) { DWORD i = 0; LOG(Succ, _T("Starting")); // // Options parsing // LOG(Succ, _T("Parsing options")); ACE_FILTER_OPTIONS options = { 0 }; DWORD importerAce = PLUGIN_MAX_IMPORTERS; DWORD importerObj = PLUGIN_MAX_IMPORTERS; DWORD importerSch = PLUGIN_MAX_IMPORTERS; if (argc > 1) { ParseOptions(&options, argc, argv); } else { options.misc.showHelp = TRUE; } if (options.misc.showHelp) { Usage(argv[0], NULL); ForeachLoadedPlugins(&options, PluginHelp); ExitProcess(EXIT_FAILURE); } // // Plugins verifications // LOG(Succ, "Verifying and choosing plugins"); if (options.plugins.numberOfImporters == 0){ FATAL(_T("No importer has been loaded")); } if (options.plugins.numberOfWriters == 0){ FATAL(_T("No writer has been loaded")); } for (i = 0; i < options.plugins.numberOfImporters; i++) { if (!PLUGIN_IS_LOADED(&options.plugins.importers[i])) { FATAL(_T("Importer <%u> is registered, but not loaded"), i); } if (importerAce == PLUGIN_MAX_IMPORTERS && options.plugins.importers[i].functions.GetNextAce) { LOG(Info, SUB_LOG(_T("Using <%s> to import ACE")), PLUGIN_GET_NAME(&options.plugins.importers[i])); importerAce = i; } if (importerObj == PLUGIN_MAX_IMPORTERS && options.plugins.importers[i].functions.GetNextSchema) { LOG(Info, SUB_LOG(_T("Using <%s> to import objects")), PLUGIN_GET_NAME(&options.plugins.importers[i])); importerObj = i; } if (importerSch == PLUGIN_MAX_IMPORTERS && options.plugins.importers[i].functions.GetNextObject) { LOG(Info, SUB_LOG(_T("Using <%s> to import schema")), PLUGIN_GET_NAME(&options.plugins.importers[i])); importerSch = i; } } if (importerAce == PLUGIN_MAX_IMPORTERS){ FATAL(_T("ACE importer is missing")); } if (importerObj == PLUGIN_MAX_IMPORTERS){ FATAL(_T("Obj importer is missing")); } if (importerSch == PLUGIN_MAX_IMPORTERS){ FATAL(_T("Sch importer is missing")); } // We allow no filter to be loaded, to permit ACE format conversion (direct link importer->writer) if (options.plugins.numberOfFilters > 0) { for (i = 0; i < options.plugins.numberOfFilters; i++) { if (!PLUGIN_IS_LOADED(&options.plugins.filters[i])) { FATAL(_T("Filter <%u> is registered, but not loaded"), i); } } } for (i = 0; i < options.plugins.numberOfWriters; i++) { if (!PLUGIN_IS_LOADED(&options.plugins.writers[i])) { FATAL(_T("Writer <%u> is registered, but not loaded"), i); } } // // Initializing plugins // LOG(Succ, _T("Initializing plugins")); ForeachLoadedPlugins(&options, PluginInitialize); // // Constructing caches // LOG(Succ, _T("Constructing caches")); CachesInitialize(); if (PluginsRequires(&options, OPT_REQ_SID_RESOLUTION) || PluginsRequires(&options, OPT_REQ_DN_RESOLUTION)) { LOG(Info, SUB_LOG(_T("Plugins require SID or DN resolution, constructing object cache"))); ConstructObjectCache(&options.plugins.importers[importerObj]); LOG(Info, SUB_LOG(_T("Object cache count : <by-sid:%u> <by-dn:%u>")), CacheObjectBySidCount(), CacheObjectByDnCount()); } if (PluginsRequires(&options, OPT_REQ_GUID_RESOLUTION) || PluginsRequires(&options, OPT_REQ_CLASSID_RESOLUTION) || PluginsRequires(&options, OPT_REQ_DISPLAYNAME_RESOLUTION)) { LOG(Info, SUB_LOG(_T("Plugins require GUID or CLASSID or DisplayName resolution, constructing schema cache"))); ConstructSchemaCache(&options.plugins.importers[importerSch]); LOG(Info, SUB_LOG(_T("Schema cache count : <by-guid:%u> <by-classid:%u> <by-displayname:%u>")), CacheSchemaByGuidCount(), CacheSchemaByClassidCount(), CacheSchemaByDisplayNameCount()); } if (PluginsRequires(&options, OPT_REQ_ADMINSDHOLDER_SD)) { LOG(Info, SUB_LOG(_T("Plugins require AdminSdHolder security descriptor, constructing it"))); ConstructAdminSdHolderSD(&options.plugins.importers[importerAce]); } // // Main Loop : process and filter ACEs // LOG(Succ, _T("Starting ACE filtering")); IMPORTED_ACE ace = { 0 }; BOOL passedFilters = TRUE; BOOL filterRet = FALSE; // Stats variables DWORD aceCount = 0; DWORD keptAceCount = 0; ULONGLONG timeStart = 0; timeStart = GetTickCount64(); options.plugins.importers[importerAce].functions.ResetReading(&gc_PluginApiTable, ImporterAce); while (options.plugins.importers[importerAce].functions.GetNextAce(&gc_PluginApiTable, &ace)) { passedFilters = TRUE; ace.computed.number = ++aceCount; for (i = 0; i < options.plugins.numberOfFilters; i++) { filterRet = options.plugins.filters[i].functions.FilterAce(&gc_PluginApiTable, &ace); passedFilters &= options.plugins.filters[i].inverted ? !filterRet : filterRet; if (!passedFilters) { LOG(All, _T("Ace <%u> was filtered by <%s>"), aceCount, PLUGIN_GET_NAME(&options.plugins.filters[i])); break; } } if (passedFilters) { keptAceCount++; LOG(All, _T("Ace <%u> passed all filters"), aceCount); for (i = 0; i < options.plugins.numberOfWriters; i++) { options.plugins.writers[i].functions.WriteAce(&gc_PluginApiTable, &ace); } } if (aceCount % options.misc.progression == 0) { LOG(Info, SUB_LOG(_T("Count : %u")), aceCount); } FreeCheckX(ace.imported.objectDn); // TODO : possible leak, since this is importer-dependant. Call importer's destroyer instead. LocalFreeCheckX(ace.imported.raw); ZeroMemory(&ace, sizeof(IMPORTED_ACE)); } // // Stats // LOG(Succ, _T("Done. ACE Statistics :")); LOG(Succ, SUB_LOG(_T("<total : %u>")), aceCount); LOG(Succ, SUB_LOG(_T("<filtered : %06.2f%% %u>")), PERCENT((aceCount - keptAceCount), aceCount), (aceCount - keptAceCount)); LOG(Succ, SUB_LOG(_T("<kept : %06.2f%% %u>")), PERCENT(keptAceCount, aceCount), keptAceCount); LOG(Succ, SUB_LOG(_T("<time : %.3fs>")), TIME_DIFF_SEC(timeStart, GetTickCount64())); // // Finalizing and unloading plugins // LOG(Succ, _T("Finalizing and unloading plugins")); ForeachLoadedPlugins(&options, PluginFinalize); ForeachLoadedPlugins(&options, PluginUnload); CachesDestroy(); DestroyStrPairList(gs_PluginOptions.head); // // End // LOG(Succ, _T("Done")); return EXIT_SUCCESS; }
static void ConstructSchemaCache( _In_ PPLUGIN_IMPORTER importer ) { IMPORTED_SCHEMA sch = { 0 }; DWORD schCount = 0; if (!importer->functions.GetNextSchema) { FATAL(_T("Schema cache is required but importer <%s> does allows Schema importation"), PLUGIN_GET_NAME(importer)); } importer->functions.ResetReading(&gc_PluginApiTable, ImporterSchema); while (importer->functions.GetNextSchema(&gc_PluginApiTable, &sch)) { sch.computed.number = ++schCount; CacheInsertSchema(&sch); FreeCheckX(sch.imported.dn); // TODO : possible leak, since this is importer-dependant. Call importer's destroyer instead. FreeCheckX(sch.imported.defaultSecurityDescriptor); ZeroMemory(&sch, sizeof(IMPORTED_SCHEMA)); } CacheActivateSchemaCache(); }
static void ConstructObjectCache( _In_ PPLUGIN_IMPORTER importer ) { IMPORTED_OBJECT obj = { 0 }; DWORD objCount = 0; if (!importer->functions.GetNextObject) { FATAL(_T("Object cache is required but importer <%s> does allows Object importation"), PLUGIN_GET_NAME(importer)); } importer->functions.ResetReading(&gc_PluginApiTable, ImporterObject); while (importer->functions.GetNextObject(&gc_PluginApiTable, &obj)) { obj.computed.number = ++objCount; CacheInsertObject(&obj); FreeCheckX(obj.imported.dn); // TODO : possible leak, since this is importer-dependant. Call importer's destroyer instead. ZeroMemory(&obj, sizeof(IMPORTED_OBJECT)); } CacheActivateObjectCache(); }