// /file SNFMilterConfig.cpp // // Copyright (C) 2011, ARM Research Labs, LLC. // See www.armresearch.com for the copyright terms. // // This file contains the functions for SNFMilterConfig. // // $Id$ // /////////////////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SNFMilterConfig.hpp" using namespace std; ////////////////////////////////////////////////////////////////////////////////////////////////////////// // Configuration. //////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// // Initialize default configuration file path. #ifdef WIN // Windows OS. const std::string SNFMilterConfig::DefaultConfigFile("C:\\SNF\\SNFMilter.xml"); const std::string SNFMilterConfig::DefaultSampleConfigFile("C:\\SNF\\SNFMilter.xml.sample"); #else #ifdef DEFAULT_CONFIG_DIR // *nix, DEFAULT_CONFIG_DIR is specified on the compile command line. const std::string SNFMilterConfig::DefaultConfigFile(DEFAULT_CONFIG_DIR "/snf-milter/SNFMilter.xml"); const std::string SNFMilterConfig::DefaultSampleConfigFile(DEFAULT_CONFIG_DIR "/snf-milter/SNFMilter.xml.sample"); #else // Not Windows, and DEFAULT_CONFIG_DIR is not specified on the compile // command line. In this case, we don't know the default path for the // configuration file. const std::string SNFMilterConfig::DefaultConfigFile(""); const std::string SNFMilterConfig::DefaultSampleConfigFile(""); #endif #endif const string ConfigFileKey("-config="); const string IntegrateWithNoneKey("-mta=none"); const string IntegrateWithPostfixKey("-mta=postfix"); const string IntegrateWithSendmailKey("-mta=sendmail"); const string SnfMilterMainCfSearchString("Added by SNFMilterConfig"); const string SnfMilterMainCfIntegrationString("smtpd_milters = unix:/var/snf-milter/socket $smtpd_milters # Added by SNFMilterConfig"); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // End of configuration. ///////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// void SNFMilterConfig::DisplayHelp(std::string Version) { cout << Version << endl << "Copyright (C) 2012, ARM Research Labs, LLC (www.armresearch.com)\n\n" << "Usage:\n\n" << "SNFMilterConfig [" << ConfigFileKey << "snf-config-file] " << IntegrateWithPostfixKey << " | " << IntegrateWithSendmailKey << " | " << IntegrateWithNoneKey << " " << UtilityConfig::HelpCommandLine() << "\n\n" << "SNFMilterConfig creates the configuration files (snf-config-file and the ignore list file) and the\n" << "rulebase download script (default: getRulebase) if they don't exist.\n\n" << " -config=snf-config-file Specifies the configuration file\n" << " -mta=postfix Integrate with postfix\n" << " -mta=sendmail Integrate with sendmail\n" << " -mta=none Remove any integration with all supported MTAs\n" << UtilityConfig::HelpDescription() << "\n" << "If snf-config-file is not specified, then the following file is used:\n\n" << " " << DefaultConfigFile << "\n\n"; }; bool SNFMilterConfig::GetCommandLineInput(int argc, char* argv[]) { int i; int NumCommandsFound = 0; string OneInput; string ConfigFile; for (i = 1; i < argc; i++) { // Check each input. OneInput = argv[i]; if (0 == OneInput.find(ConfigFileKey)) { ConfigFile = OneInput.substr(ConfigFileKey.length()); } else if (OneInput == IntegrateWithNoneKey) { Command = IntegrateWithNoneCmd; NumCommandsFound++; } else if (OneInput == IntegrateWithPostfixKey) { Command = IntegrateWithPostfixCmd; NumCommandsFound++; } else if (0 == OneInput.find(IntegrateWithSendmailKey)) { Command = IntegrateWithSendmailCmd; NumCommandsFound++; } else { // Process command-line input by the base class. if (!ProcessCommandLineItem(OneInput)) { return false; // Illegal input. } } } if (0 == ConfigFile.length()) { // Load default config file name. ConfigFile = DefaultConfigFile; } LoadConfigFile(ConfigFile); LoadInfo(); // Load the file paths. LoadSocketInfo(); // Load the socket path. return (NumCommandsFound == 1); } void SNFMilterConfig::LoadSocketInfo() { std::string MilterElement = GetPlatformContents(); ConfigurationData PlatformConfig(MilterElement.c_str(), MilterElement.length()); ConfigurationElement SocketReader("milter"); SocketReader .Element("socket") .Attribute("path", SocketFileName) .End("socket") .End("milter"); SocketReader.interpret(PlatformConfig); } void SNFMilterConfig::CreateSocketDir() { std::string SocketDir; std::string::size_type LastDirSepIndex = SocketFileName.rfind("/"); SocketDir = ( (std::string::npos == LastDirSepIndex) ? SocketFileName : SocketFileName.substr(0, LastDirSepIndex)); if (Verbose()) { cout << "Create the milter socket directory " << SocketDir << "..."; } if (!Explain()) { if (!FileExists(SocketDir)) { MkDir(SocketDir); } SetMode(SocketDir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP); SetOwnerGroup(SocketDir); } OutputVerboseEnd(); } void SNFMilterConfig::UpdateConfigFiles() { std::string ConfigFileName = GetConfigFileName(); if (!FileExists(ConfigFileName)) { Copy(DefaultSampleConfigFile, ConfigFileName); // Use SNFMilter.xml.sample. } SetMode(ConfigFileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); // Set permissions. SetOwnerGroup(ConfigFileName); // Set to sniffer user. UpdateLogDir(); UpdateIgnoreListFile(); } void SNFMilterConfig::DoIntegrationCommand() { switch (Command) { case IntegrateWithNoneCmd: UnintegrateWithAll(); break; case IntegrateWithPostfixCmd: IntegrateWithPostfix(); break; case IntegrateWithSendmailCmd: IntegrateWithSendmail(); break; default: { ostringstream Temp; Temp << "Internal error in SNFMilterConfig::DoCommand: Invalid value of command: " << Command; throw runtime_error(Temp.str()); } } } void SNFMilterConfig::UnintegrateWithAll() { UnintegrateWithPostfix(); UnintegrateWithSendmail(); // TODO: Restart MTA. // Do not remove the socket directory; users might have placed // files in it. This happened with the /tmp directory; it was // supposed to be only for files that would be deleted on reboot. // However, admins stored files that they wished to be persistent // across reboots in /tmp. } void SNFMilterConfig::IntegrateWithPostfix() { UnintegrateWithAll(); // Remove any existing integration. if (Verbose()) { cout << "Add to postfix file " << PostfixMainCfPath << ": '" << SnfMilterMainCfIntegrationString << "'..."; } if (!Explain()) { ofstream Output; // Append the configuration. Output.open(PostfixMainCfPath.c_str(), ios::app); if (!Output) { string Temp; Temp = "Error opening the postfix configuration file " + PostfixMainCfPath; Temp += " for writing: "; Temp += strerror(errno); throw runtime_error(Temp); } Output << SnfMilterMainCfIntegrationString << "\n"; if (!Output) { string Temp; Temp = "Error appending to the postfix configuration file " + PostfixMainCfPath; Temp += ": "; Temp += strerror(errno); throw runtime_error(Temp); } Output.close(); if (!Output) { string Temp; Temp = "Error closing the postfix configuration file " + PostfixMainCfPath; Temp += " after appending: "; Temp += strerror(errno); throw runtime_error(Temp); } } OutputVerboseEnd(); CreateSocketDir(); StartOrRestartMta("postfix"); } void SNFMilterConfig::UnintegrateWithPostfix() { ifstream Input; if (Verbose()) { cout << "Remove any integration in postfix file " << PostfixMainCfPath << "--\n"; } Input.open(PostfixMainCfPath.c_str()); // Read the contents. if (!Input) { string Temp; Temp = "Error opening the postfix configuration file " + PostfixMainCfPath; Temp += " for reading: "; Temp += strerror(errno); throw runtime_error(Temp); } string Content; string Line; while (getline(Input, Line)) { if (string::npos != Line.find(SnfMilterMainCfSearchString)) { // Check for integration line. if (Verbose()) { cout << " Remove '" << Line << "'...\n"; } continue; // Do not copy this line. } Content += Line + "\n"; // Copy this line. } if (!Input.eof()) { // Should be at end-of-file. string Temp; Temp = "Error reading the postfix configuration file " + PostfixMainCfPath; Temp += ": "; Temp += strerror(errno); throw runtime_error(Temp); } Input.close(); if (Input.bad()) { string Temp; Temp = "Error closing the postfix configuration file " + PostfixMainCfPath; Temp += " after reading: "; Temp += strerror(errno); throw runtime_error(Temp); } if (!Explain()) { ofstream Output; // Write the updated contents. Output.open(PostfixMainCfPath.c_str(), ios::trunc); if (!Output) { string Temp; Temp = "Error opening the postfix configuration file " + PostfixMainCfPath; Temp += " for writing: "; Temp += strerror(errno); throw runtime_error(Temp); } Output << Content; if (!Output) { string Temp; Temp = "Error writing the postfix configuration file " + PostfixMainCfPath; Temp += ": "; Temp += strerror(errno); throw runtime_error(Temp); } Output.close(); if (!Output) { string Temp; Temp = "Error closing the postfix configuration file " + PostfixMainCfPath; Temp += " after writing: "; Temp += strerror(errno); throw runtime_error(Temp); } } OutputVerboseEnd(); } void SNFMilterConfig::IntegrateWithSendmail() { throw runtime_error("Integration with sendmail is not implemented"); } void SNFMilterConfig::UnintegrateWithSendmail() { std::cerr << "Unintegration with sendmail is not implemented" << "\n"; }