// /file PostfixIntegrate.cpp // // Copyright (C) 2013, ARM Research Labs, LLC. // See www.armresearch.com for the copyright terms. // // This file contains the functions for PostfixIntegrate. // // $Id$ // /////////////////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include "PostfixIntegrate.hpp" ////////////////////////////////////////////////////////////////////////////////////////////////////////// // Configuration. //////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// // End of configuration. ///////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// void PostfixIntegrate::SetOperatingSystem(std::string OperatingSystemType) { MtaIsRunningCommand = "ps axl | grep -v grep | grep -q 'postfix/master'"; PostfixDefaultIsChrooted = false; // Overwritten if postfix is SnfSnifferDirName = "/usr/sbin"; // by default chrooted. SnfSnifferFileName = SnfSnifferDirName + "/snfSniffer"; SnfSnifferSampleFileName = SnfSnifferFileName + ".sample"; ContentFilterLine = " -o content_filter=snfilter:dummy\n"; ContentFilterSpec = "snfilter unix - n n - 10 pipe\n"; if ("OpenBSD" == OperatingSystemType) { PostfixDefaultIsChrooted = true; SnfSnifferDirName = "/usr/local/sbin"; SnfSnifferFileName = SnfSnifferDirName + "/snfSniffer"; SnfSnifferSampleFileName = "/usr/local/sbin/snfSniffer.sample"; ContentFilterSpec += " flags=Rq user=snfuser argv=/usr/local/sbin/snfSniffer\n"; PostfixMainCfPath = "/etc/postfix/main.cf"; PostfixMasterCfPath = "/etc/postfix/master.cf"; ReloadMtaCommand = "/usr/local/sbin/postfix reload"; } else if ("FreeBSD" == OperatingSystemType) { ContentFilterSpec += " flags=Rq user=snfuser argv=/usr/sbin/snfSniffer\n"; SnfSnifferDirName = "/usr/local/sbin"; SnfSnifferFileName = SnfSnifferDirName + "/snfSniffer"; SnfSnifferSampleFileName = "/usr/local/sbin/snfSniffer.sample"; PostfixMainCfPath = "/usr/local/etc/postfix/main.cf"; PostfixMasterCfPath = "/usr/local/etc/postfix/master.cf"; ReloadMtaCommand = "/usr/local/sbin/postfix reload"; } else if ("Ubuntu" == OperatingSystemType) { PostfixDefaultIsChrooted = true; SnfSnifferFileName = SnfSnifferDirName + "/snfSniffer"; SnfSnifferSampleFileName = "/usr/sbin/snfSniffer.sample"; ContentFilterSpec += " flags=Rq user=snfuser argv=/usr/sbin/snfSniffer\n"; PostfixMainCfPath = "/etc/postfix/main.cf"; PostfixMasterCfPath = "/etc/postfix/master.cf"; ReloadMtaCommand = "/usr/sbin/postfix reload"; } else if ("RedHat" == OperatingSystemType) { ContentFilterSpec += " flags=Rq user=snfuser argv=/usr/sbin/snfSniffer\n"; PostfixMainCfPath = "/etc/postfix/main.cf"; PostfixMasterCfPath = "/etc/postfix/master.cf"; ReloadMtaCommand = "/usr/sbin/postfix reload"; } else if ("Suse" == OperatingSystemType) { ContentFilterSpec += " flags=Rq user=snfuser argv=/usr/sbin/snfSniffer\n"; PostfixMainCfPath = "/etc/postfix/main.cf"; PostfixMasterCfPath = "/etc/postfix/master.cf"; ReloadMtaCommand = "/usr/sbin/postfix reload"; } else if ("ArchLinux" == OperatingSystemType) { ContentFilterSpec += " flags=Rq user=snfuser argv=/usr/sbin/snfSniffer\n"; PostfixMainCfPath = "/etc/postfix/main.cf"; PostfixMasterCfPath = "/etc/postfix/master.cf"; ReloadMtaCommand = "/usr/sbin/postfix reload"; } else { std::ostringstream Temp; Temp << "***Error from PostfixIntegrate::SetOperatingSystem: Invalid value of OperatingSystemType: " << OperatingSystemType; throw std::runtime_error(Temp.str()); } ContentFilterSpec += " -f ${sender} -- ${recipient}\n"; } void PostfixIntegrate::Integrate(FileBackup *SaveFile) { if (IsIntegrated()) { return; } // Check whether the chroot configuration is as expected. bool IsChrooted; IsChrooted = MtaConfigurationIsChrooted(); if (IsChrooted != PostfixDefaultIsChrooted) { std::string Temp; Temp = "Error--postfix must be configured to run "; Temp += (PostfixDefaultIsChrooted ? "" : "not "); Temp += "chrooted, which is the default for this operating system. "; Temp += "postfix was detected to be configured to run "; Temp += (IsChrooted ? "" : "not "); Temp += "chrooted."; throw std::runtime_error(Temp); } if (Verbose()) { std::cout << "Integrate with postfix...\n"; } std::string Content; if (!Explain()) { if (!FileExists(SnfSnifferFileName)) { // Create SnfSniffer script // if it doesn't exist. SaveFile->CreateBackupFile(SnfSnifferFileName); if (!FileExists(SnfSnifferDirName)) { MkDir(SnfSnifferDirName); } SetMode(SnfSnifferDirName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); SetOwnerGroup(SnfSnifferDirName); Copy(SnfSnifferSampleFileName, SnfSnifferFileName); SetMode(SnfSnifferFileName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); SetOwnerGroup(SnfSnifferFileName); } SaveFile->CreateBackupFile(PostfixMasterCfPath); std::ifstream Input; Input.open(PostfixMasterCfPath.c_str()); // Read the contents. if (!Input) { std::string Temp; Temp = "Error opening the postfix configuration file " + PostfixMasterCfPath; Temp += " for reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string Line; while (getline(Input, Line)) { Content += Line + "\n"; // Copy this line. if ( (Line.find("smtp") == 0) && (Line.find("inet") != std::string::npos) ) { if (Verbose()) { std::cout << " Add\n\n " << ContentFilterLine << "\n\n after\n\n" << Line << "\n\n in " << PostfixMasterCfPath << "...\n"; } Content += ContentFilterLine; } } if (!Input.eof()) { // Should be at end-of-file. std::string Temp; Temp = "Error reading the postfix configuration file " + PostfixMasterCfPath; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } Input.close(); if (Input.bad()) { std::string Temp; Temp = "Error closing the postfix configuration file " + PostfixMasterCfPath; Temp += " after reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } if (Verbose()) { std::cout << " Add\n\n" << ContentFilterSpec << "\n\n to the end of " << PostfixMasterCfPath << "...\n"; } Content += ContentFilterSpec; if (!Explain()) { std::ofstream Output; // Write the updated contents. Output.open(PostfixMasterCfPath.c_str(), std::ios::trunc); if (!Output) { std::string Temp; Temp = "Error opening the postfix configuration file " + PostfixMasterCfPath; Temp += " for writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output << Content; if (!Output) { std::string Temp; Temp = "Error writing the postfix configuration file " + PostfixMasterCfPath; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output.close(); if (!Output) { std::string Temp; Temp = "Error closing the postfix configuration file " + PostfixMasterCfPath; Temp += " after writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } } } OutputVerboseEnd(); if (!ReloadMta()) { std::cerr << "Unable to reload the postfix configuration. Please run " << "'postfix reload' for the integration with SNFServer to take effect."; } } void PostfixIntegrate::Unintegrate(FileBackup *SaveFile) { if (!IsIntegrated()) { return; } std::ifstream Input; if (Verbose()) { std::cout << "Remove integration in postfix file " << PostfixMasterCfPath << "--\n"; } if (!Explain()) { SaveFile->CreateBackupFile(PostfixMasterCfPath); // Save any existing file. Input.open(PostfixMasterCfPath.c_str()); // Read the contents. if (!Input) { std::string Temp; Temp = "Error opening the postfix configuration file " + PostfixMasterCfPath; Temp += " for reading: "; 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 postfix configuration file " + PostfixMasterCfPath; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string Content; Content = ContentStream.str(); if (Verbose()) { std::cout << " Remove all occurances of\n\n" << ContentFilterLine << "\n\n" << " from" << PostfixMasterCfPath << "...\n"; } std::string::size_type IntegrationBegin = std::string::npos; while ((IntegrationBegin = Content.find(ContentFilterLine)) != std::string::npos) { Content.erase(IntegrationBegin, ContentFilterLine.length()); } if (Verbose()) { std::cout << " Remove all occurances of\n\n" << ContentFilterSpec << "\n\n" << " from" << PostfixMasterCfPath << "...\n"; } IntegrationBegin = std::string::npos; while ((IntegrationBegin = Content.find(ContentFilterSpec)) != std::string::npos) { Content.erase(IntegrationBegin, ContentFilterSpec.length()); } std::ofstream Output; // Write the updated contents. Output.open(PostfixMasterCfPath.c_str(), std::ios::trunc); if (!Output) { std::string Temp; Temp = "Error opening the postfix configuration file " + PostfixMasterCfPath; Temp += " for writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output << Content; if (!Output) { std::string Temp; Temp = "Error writing the postfix configuration file " + PostfixMasterCfPath; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output.close(); if (!Output) { std::string Temp; Temp = "Error closing the postfix configuration file " + PostfixMasterCfPath; Temp += " after writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } } OutputVerboseEnd(); if (!ReloadMta()) { std::cerr << "Unable to reload the postfix configuration. Please run " << "'postfix reload' for the integration with SNFServer to take effect."; } } bool PostfixIntegrate::MtaIsRunningDetected() { if (Verbose()) { std::cout << "Checking whether postfix is detected to be running..."; } bool IsRunningDetected; IsRunningDetected = (std::system(MtaIsRunningCommand.c_str()) == 0); if (Verbose()) { std::cout << (IsRunningDetected ? "yes..." : "no..."); } OutputVerboseEnd(); return IsRunningDetected; } bool PostfixIntegrate::ReloadMta() { if (!MtaIsRunningDetected()) { return true; } if (Verbose()) { std::cout << "Reloading postfix...\n"; std::cout.flush(); } bool Succeeded; if (!Explain()) { Succeeded = (std::system(ReloadMtaCommand.c_str()) == 0); if (Verbose()) { std::cout << (Succeeded ? "succeeded..." : "failed..."); } } OutputVerboseEnd(); return Succeeded; } bool PostfixIntegrate::IsIntegrated() { if (Verbose()) { std::cout << "Checking for any SNFServer integration in the postfix file " << PostfixMasterCfPath << "..."; } if (!FileExists(PostfixMasterCfPath)) { if (Verbose()) { std::cout << "file doesn't exist; postfix is not integrated..."; } OutputVerboseEnd(); return false; } std::ifstream Input; Input.open(PostfixMasterCfPath.c_str()); // Read the contents. if (!Input) { std::string Temp; Temp = "Error opening the postfix configuration file " + PostfixMasterCfPath; Temp += " for reading: "; 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 postfix configuration file " + PostfixMasterCfPath; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string Content; Content = ContentStream.str(); bool FoundContentFilterLine = (Content.find(ContentFilterLine) != std::string::npos); bool FoundContentFilterSpec = (Content.find(ContentFilterSpec) != std::string::npos); bool Integrated = (FoundContentFilterLine || FoundContentFilterSpec); if (Verbose()) { if (FoundContentFilterLine) { std::cout << "found\n\n" << ContentFilterLine << "\n\n"; } if (FoundContentFilterSpec) { std::cout << "found\n\n" << ContentFilterSpec << "\n\n"; } if (!Integrated) { std::cout << "none found..."; } } OutputVerboseEnd(); return Integrated; } bool PostfixIntegrate::DefaultIsChrooted() { return PostfixDefaultIsChrooted; } bool PostfixIntegrate::MtaConfigurationIsChrooted() { std::string File; std::ifstream Input; File = PostfixMasterCfPath; Input.open(File.c_str()); if (!Input) { std::string Temp; Temp = "Error opening postfix configuration file " + File; Temp += " for reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string Line; bool ConfigurationIsChrooted = false; while (getline(Input, Line)) { if (CheckForString(Line, "smtp")) { // Check for smtp line. std::istringstream Buffer(Line); // Parse buffer line. std::string Token[8]; for (unsigned int iToken = 0; iToken < 8; iToken++) { Buffer >> Token[iToken]; } if ( ("y" == Token[4]) || ("-" == Token[4]) ) { ConfigurationIsChrooted = true; break; } } } Input.close(); if (Input.bad()) { std::string Temp; Temp = "Error closing the postfix configuration file " + File; Temp += " after reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } return ConfigurationIsChrooted; }