// /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::SampleConfigFile("C:\\SNF\\SNFMilter.xml.sample"); const std::string SNFMilterConfig::SampleIdentityFile("C:\\SNF\\identity.xml.sample"); const std::string InstallFile(""); #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::SampleConfigFile(DEFAULT_CONFIG_DIR "/snf-milter/SNFMilter.xml.sample"); const std::string SNFMilterConfig::SampleIdentityFile(DEFAULT_CONFIG_DIR "/snf-milter/identity.xml.sample"); const std::string InstallFile(DOC_DIR "/INSTALL"); #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::SampleConfigFile(""); const std::string SNFMilterConfig::SampleIdentityFile(""); const std::string InstallFile("INSTALL"); #endif #endif const string SNFMilterConfig::ApplicationName("SNFMilter"); const std::string SNFMilterConfig::DefaultSocketFileName("/var/snf-milter/socket"); const std::string SNFMilterConfig::DefaultPostfixIsChrootedSocketFileName("/var/spool/postfix/snf-milter/socket"); const string IntegrateWithNoneKey("-with=none"); const string IntegrateWithPostfixKey("-with=postfix"); const string IntegrateWithSendmailKey("-with=sendmail"); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // End of configuration. ///////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// void SNFMilterConfig::DisplayHelp(std::string Version) { std::string ExclusiveCommands; std::string ExclusiveCommandsHelp; ExclusiveCommands = IntegrateWithPostfixKey + " | "; ExclusiveCommands += IntegrateWithSendmailKey + " | "; ExclusiveCommands += IntegrateWithNoneKey; ExclusiveCommandsHelp = " -with=postfix Integrate with postfix and start/reload postfix\n"; ExclusiveCommandsHelp += " -with=sendmail Integrate with sendmail and start/reload sendmail\n"; ExclusiveCommandsHelp += " (Not available on OpenBSD or FreeBSD)\n"; ExclusiveCommandsHelp += " -with=none Remove any integration with all supported MTAs\n"; cout << Version << endl << "Copyright (C) 2012, ARM Research Labs, LLC (www.armresearch.com)\n\n" << "Usage:\n\n" << "SNFMilterConfig " << UtilityConfig::HelpCommandLine(ExclusiveCommands) << "\n\n" << "SNFMilterConfig " << UtilityConfig::HelpDescription(ExclusiveCommandsHelp) << "\n" << "The configuration file name is:\n\n" << " " << DefaultConfigFile << "\n\n" << "If the above file doesn't exist, then it is copied from the following file:\n\n" << " " << SampleConfigFile << "\n\n" << "If integration with an MTA is specified, the MTA's configuration is reloaded " << "if the MTA is running.\n\n"; }; bool SNFMilterConfig::GetCommandLineInput(int argc, char* argv[]) { int i; int NumCommandsFound = 0; string OneInput; Command = NoCommand; // Default is to do nothing. for (i = 1; i < argc; i++) { // Check each input. OneInput = argv[i]; if (OneInput == IntegrateWithPostfixKey) { Command = IntegrateWithPostfixCommand; NumCommandsFound++; } else if (0 == OneInput.find(IntegrateWithSendmailKey)) { std::string OsType; OsType = GetOperatingSystemType(); // Check whether the platform is supported. if ( ("OpenBSD" == OsType) || ("FreeBSD" == OsType) ) { std::string Temp; Temp = "Integration with sendmail is not supported on " + OsType; Temp += ".\n"; Temp += "Please see " + InstallFile; Temp += " for information on integration with sendmail.\n"; throw std::runtime_error(Temp); } Command = IntegrateWithSendmailCommand; NumCommandsFound++; } else if (OneInput == IntegrateWithNoneKey) { Command = IntegrateWithNoneCommand; NumCommandsFound++; } else { // Process command-line input by the base class. if (!ProcessCommandLineItem(OneInput)) { Command = UnknownCommand; return false; // Illegal input. } } } if (UpdateCredentialsSpecified()) { Command = UpdateCredentialsCommand; NumCommandsFound++; } if (SetupRepairSpecified()) { Command = SetupRepairCommand; NumCommandsFound++; } if (StartSnifferSpecified()) { Command = StartSnifferCommand; NumCommandsFound++; } if (StopSnifferSpecified()) { Command = StopSnifferCommand; NumCommandsFound++; } return ( (NumCommandsFound == 1) && CommandLineIsOkay() ); } void SNFMilterConfig::ExecuteCommand() { Postfix.SetOperatingSystem(GetOperatingSystemType()); Postfix.SetVerbose(Verbose()); Postfix.SetExplain(Explain()); Sendmail.SetOperatingSystem(GetOperatingSystemType()); Sendmail.SetVerbose(Verbose()); Sendmail.SetExplain(Explain()); SetConfigFileName(DefaultConfigFile); if (SetupRepairCommand == Command) { CreateDefaultConfigFile(SampleConfigFile); // Create the file if it doesn't exist, // Set owner and mode in any case. } LoadConfig(); LoadInfo(); // Load the file paths. LoadSocketInfo(); // Load the socket path. switch (Command) { case SetupRepairCommand: SetupRepair(SampleIdentityFile); SetupRepairSocketDir(); break; case UpdateCredentialsCommand: UpdateRulebaseScriptCredentials(); DownloadRulebase(); UpdateIdentityFile(); break; case IntegrateWithPostfixCommand: Postfix.Integrate(&SaveFile); UnintegrateWithAllExcept("postfix"); if (Postfix.DefaultIsChrooted()) { // Update Sniffer file. SetSocketFileName(DefaultPostfixIsChrootedSocketFileName); // Update Sniffer configuration with chrooted socket spec. LoadConfig(); // Create the socket in the chrooted location. LoadSocketInfo(); SetupRepairSocketDir(); } else { SetSocketFileName(DefaultSocketFileName); // Update Sniffer configuration with non-chrooted socket spec. } break; case IntegrateWithSendmailCommand: Sendmail.Integrate(&SaveFile); UnintegrateWithAllExcept("sendmail"); // Update Sniffer configuration with non-chrooted socket spec. SetSocketFileName(DefaultSocketFileName); break; case IntegrateWithNoneCommand: UnintegrateWithAllExcept(); SetSocketFileName(DefaultSocketFileName); // Update Sniffer configuration with non-chrooted socket spec. break; case StartSnifferCommand: LoadCredentials(); StartSniffer("snf-milter start", ApplicationName); break; case StopSnifferCommand: LoadCredentials(); StopSniffer("snf-milter stop", ApplicationName); break; default: break; } } void SNFMilterConfig::SetSocketFileName(std::string NewSocketFileName) { std::string File = GetConfigFileName(); if (Verbose()) { cout << "Setting socket to '" << NewSocketFileName << "' in '" << File << "'..."; } if (!Explain()) { std::ifstream Input; Input.open(File.c_str()); if (!Input) { std::string Temp; Temp = "Error opening the configuration file " + File; Temp += " to read from: "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::ostringstream ContentStream; ContentStream << Input.rdbuf(); Input.close(); if (!Input) { std::string Temp; Temp = "Error closing the configuration file " + File; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } if (ContentStream.bad() || ContentStream.fail()) { std::string Temp; Temp = "Error reading the configuration file " + File; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string Content; Content = ContentStream.str(); std::string ElementName = "socket"; std::string AttributeName = "path"; ReplaceXmlAttribute(&Content, ElementName, AttributeName, NewSocketFileName); std::ofstream Output; Output.open(File.c_str(), std::ios::trunc); if (!Output) { std::string Temp; Temp = "Error opening the configuration file " + File; Temp += " to write to: "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output << Content; if (Output.bad() || Output.fail()) { std::string Temp; Temp = "Error writing the configuration file " + File; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output.close(); if (!Output) { std::string Temp; Temp = "Error closing the configuration file " + File; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } } OutputVerboseEnd(); } 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::SetupRepairSocketDir() { 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_IXGRP); SetOwnerGroup(SocketDir); } OutputVerboseEnd(); } void SNFMilterConfig::SaveFileState() { if (!Explain()) { SaveFile.CreateBackupFile(GetRulebaseScriptName()); if (UpdateCredentialsSpecified()) { SaveFile.CreateBackupFile(GetRulebaseFileName()); } SaveFile.CreateBackupFile(GetIdentityFileName()); SaveFile.CreateBackupFile(GetIgnoreListFileName()); } } void SNFMilterConfig::UnintegrateWithAllExcept(std::string Except) { if (Except != "postfix") { Postfix.Unintegrate(&SaveFile); } if (Except != "sendmail") { Sendmail.Unintegrate(&SaveFile); } }