You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PostfixIntegrate.cpp 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. // /file PostfixIntegrate.cpp
  2. //
  3. // Copyright (C) 2011, ARM Research Labs, LLC.
  4. // See www.armresearch.com for the copyright terms.
  5. //
  6. // This file contains the functions for PostfixIntegrate.
  7. //
  8. // $Id$
  9. //
  10. ///////////////////////////////////////////////////////////////////////////////////////////////////
  11. #include <cstdlib>
  12. #include <cerrno>
  13. #include <cstring>
  14. #include <iostream>
  15. #include <exception>
  16. #include <stdexcept>
  17. #include <sstream>
  18. #include <fstream>
  19. #include "PostfixIntegrate.hpp"
  20. #include "PostfixMilterConf.hpp"
  21. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  22. // Configuration. ////////////////////////////////////////////////////////////////////////////////////////
  23. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  24. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  25. // End of configuration. /////////////////////////////////////////////////////////////////////////////////
  26. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  27. void
  28. PostfixIntegrate::SetOperatingSystem(std::string OperatingSystemType) {
  29. MtaIsRunningCommand = "ps axl | grep -v grep | grep -q 'postfix/master'";
  30. if ("OpenBSD" == OperatingSystemType) {
  31. PostfixDefaultIsChrooted = true;
  32. PostfixSocketSpec = "unix:/snf-milter/socket";
  33. PostfixMainCfPath = "/etc/postfix/main.cf";
  34. PostfixMasterCfPath = "/etc/postfix/master.cf";
  35. ReloadMtaCommand = "/usr/local/sbin/postfix reload";
  36. AddPostfixToSnfuserGroupCommand = "/usr/sbin/usermod -G snfuser _postfix";
  37. } else if ("FreeBSD" == OperatingSystemType) {
  38. PostfixDefaultIsChrooted = false;
  39. PostfixSocketSpec = "unix:/var/snf-milter/socket";
  40. PostfixMainCfPath = "/usr/local/etc/postfix/main.cf";
  41. PostfixMasterCfPath = "/usr/local/etc/postfix/master.cf";
  42. ReloadMtaCommand = "/usr/local/sbin/postfix reload";
  43. AddPostfixToSnfuserGroupCommand = "/usr/sbin/pw mod group -n snfuser -m postfix";
  44. } else if ("Ubuntu" == OperatingSystemType) {
  45. PostfixDefaultIsChrooted = true;
  46. PostfixSocketSpec = "unix:/snf-milter/socket";
  47. PostfixMainCfPath = "/etc/postfix/main.cf";
  48. PostfixMasterCfPath = "/etc/postfix/master.cf";
  49. ReloadMtaCommand = "/usr/sbin/postfix reload";
  50. AddPostfixToSnfuserGroupCommand = "/usr/sbin/usermod -G snfuser postfix";
  51. } else if ("RedHat" == OperatingSystemType) {
  52. PostfixDefaultIsChrooted = false;
  53. PostfixSocketSpec = "unix:/var/snf-milter/socket";
  54. PostfixMainCfPath = "/etc/postfix/main.cf";
  55. PostfixMasterCfPath = "/etc/postfix/master.cf";
  56. ReloadMtaCommand = "/usr/sbin/postfix reload";
  57. AddPostfixToSnfuserGroupCommand = "/usr/sbin/usermod -G snfuser postfix";
  58. } else if ("Suse" == OperatingSystemType) {
  59. PostfixDefaultIsChrooted = false;
  60. PostfixSocketSpec = "unix:/var/snf-milter/socket";
  61. PostfixMainCfPath = "/etc/postfix/main.cf";
  62. PostfixMasterCfPath = "/etc/postfix/master.cf";
  63. ReloadMtaCommand = "/usr/sbin/postfix reload";
  64. AddPostfixToSnfuserGroupCommand = "/usr/sbin/usermod -G snfuser postfix";
  65. } else {
  66. std::ostringstream Temp;
  67. Temp << "***Error from PostfixIntegrate::SetOperatingSystem: Invalid value of OperatingSystemType: "
  68. << OperatingSystemType;
  69. throw std::runtime_error(Temp.str());
  70. }
  71. }
  72. void
  73. PostfixIntegrate::Integrate(FileBackup *SaveFile) {
  74. if (IsIntegrated()) {
  75. return;
  76. }
  77. // Check whether the chroot configuration is as expected.
  78. bool IsChrooted;
  79. IsChrooted = MtaConfigurationIsChrooted();
  80. if (IsChrooted != PostfixDefaultIsChrooted) {
  81. std::string Temp;
  82. Temp = "Error--postfix must be configured to run ";
  83. Temp += (PostfixDefaultIsChrooted ? "" : "not ");
  84. Temp += "chrooted, which is the default for this operating system. ";
  85. Temp += "postfix was detected to be configured to run ";
  86. Temp += (IsChrooted ? "" : "not ");
  87. Temp += "chrooted.";
  88. Temp += strerror(errno);
  89. throw std::runtime_error(Temp);
  90. }
  91. std::ifstream Input;
  92. if (Verbose()) {
  93. std::cout << "Integrate with postfix...add postfix user to snfuser group...\n";
  94. }
  95. bool Succeeded;
  96. if (!Explain()) {
  97. Succeeded = (std::system(AddPostfixToSnfuserGroupCommand.c_str()) == 0);
  98. if (!Succeeded) {
  99. std::string Temp;
  100. Temp = "Error adding the postfix user to the snfuser group with the command '";
  101. Temp += AddPostfixToSnfuserGroupCommand;
  102. Temp += "': ";
  103. Temp += strerror(errno);
  104. throw std::runtime_error(Temp);
  105. }
  106. SaveFile->CreateBackupFile(PostfixMainCfPath); // Save any existing file.
  107. Input.open(PostfixMainCfPath.c_str()); // Read the contents.
  108. if (!Input) {
  109. std::string Temp;
  110. Temp = "Error opening the postfix configuration file " + PostfixMainCfPath;
  111. Temp += " for reading: ";
  112. Temp += strerror(errno);
  113. throw std::runtime_error(Temp);
  114. }
  115. std::string Content;
  116. std::string Line;
  117. bool ModifiedLine = false;
  118. PostfixMilterConf MilterConf(PostfixSocketSpec); // Object to update the config line.
  119. while (getline(Input, Line)) {
  120. MilterConf.ConfLine(Line); // Load the object with the line.
  121. if (MilterConf.IsMilterLine() && !ModifiedLine) { // Check for milter integration line.
  122. // Ignore subsequence integration lines.
  123. MilterConf.AddIntegration(); // Found milter line. Add integration.
  124. if (Verbose()) {
  125. std::cout << " Replace '" << Line << "' with '"
  126. << MilterConf.ConfLine() << "'...\n";
  127. }
  128. Line = MilterConf.ConfLine(); // Copy updated line.
  129. ModifiedLine = true;
  130. }
  131. Content += Line + "\n"; // Copy this line.
  132. }
  133. if (!ModifiedLine) {
  134. MilterConf.ConfLine("");
  135. MilterConf.AddIntegration();
  136. if (Verbose()) {
  137. std::cout << " Add '" << MilterConf.ConfLine() << "'...\n";
  138. }
  139. Content += MilterConf.ConfLine() + "\n";
  140. }
  141. if (!Input.eof()) { // Should be at end-of-file.
  142. std::string Temp;
  143. Temp = "Error reading the postfix configuration file " + PostfixMainCfPath;
  144. Temp += ": ";
  145. Temp += strerror(errno);
  146. throw std::runtime_error(Temp);
  147. }
  148. Input.close();
  149. if (Input.bad()) {
  150. std::string Temp;
  151. Temp = "Error closing the postfix configuration file " + PostfixMainCfPath;
  152. Temp += " after reading: ";
  153. Temp += strerror(errno);
  154. throw std::runtime_error(Temp);
  155. }
  156. if (!Explain()) {
  157. std::ofstream Output; // Write the updated contents.
  158. Output.open(PostfixMainCfPath.c_str(), std::ios::trunc);
  159. if (!Output) {
  160. std::string Temp;
  161. Temp = "Error opening the postfix configuration file " + PostfixMainCfPath;
  162. Temp += " for writing: ";
  163. Temp += strerror(errno);
  164. throw std::runtime_error(Temp);
  165. }
  166. Output << Content;
  167. if (!Output) {
  168. std::string Temp;
  169. Temp = "Error writing the postfix configuration file " + PostfixMainCfPath;
  170. Temp += ": ";
  171. Temp += strerror(errno);
  172. throw std::runtime_error(Temp);
  173. }
  174. Output.close();
  175. if (!Output) {
  176. std::string Temp;
  177. Temp = "Error closing the postfix configuration file " + PostfixMainCfPath;
  178. Temp += " after writing: ";
  179. Temp += strerror(errno);
  180. throw std::runtime_error(Temp);
  181. }
  182. }
  183. }
  184. OutputVerboseEnd();
  185. if (!ReloadMta()) {
  186. std::cerr << "Unable to reload the postfix configuration. Please run "
  187. << "'postfix reload' for the integration with SNFMilter to take effect.";
  188. }
  189. }
  190. void
  191. PostfixIntegrate::Unintegrate(FileBackup *SaveFile) {
  192. if (!IsIntegrated()) {
  193. return;
  194. }
  195. std::ifstream Input;
  196. if (Verbose()) {
  197. std::cout << "Remove integration in postfix file " << PostfixMainCfPath << "--\n";
  198. }
  199. if (!Explain()) {
  200. SaveFile->CreateBackupFile(PostfixMainCfPath); // Save any existing file.
  201. Input.open(PostfixMainCfPath.c_str()); // Read the contents.
  202. if (!Input) {
  203. std::string Temp;
  204. Temp = "Error opening the postfix configuration file " + PostfixMainCfPath;
  205. Temp += " for reading: ";
  206. Temp += strerror(errno);
  207. throw std::runtime_error(Temp);
  208. }
  209. std::string Content;
  210. std::string Line;
  211. PostfixMilterConf MilterConf(PostfixSocketSpec); // Object to update the config line.
  212. while (getline(Input, Line)) {
  213. MilterConf.ConfLine(Line); // Load the object with the line.
  214. if (MilterConf.IsIntegrated()) { // Check for integration.
  215. MilterConf.RemoveIntegration(); // Integrated. Remove the milter spec.
  216. if (Verbose()) {
  217. std::cout << " Replace '" << Line << "' with '"
  218. << MilterConf.ConfLine() << "'...\n";
  219. }
  220. Content += MilterConf.ConfLine(); // Copy updated line.
  221. if (MilterConf.ConfLine() != "") {
  222. Content += "\n";
  223. }
  224. continue;
  225. }
  226. Content += Line + "\n"; // Copy this line.
  227. }
  228. if (!Input.eof()) { // Should be at end-of-file.
  229. std::string Temp;
  230. Temp = "Error reading the postfix configuration file " + PostfixMainCfPath;
  231. Temp += ": ";
  232. Temp += strerror(errno);
  233. throw std::runtime_error(Temp);
  234. }
  235. Input.close();
  236. if (Input.bad()) {
  237. std::string Temp;
  238. Temp = "Error closing the postfix configuration file " + PostfixMainCfPath;
  239. Temp += " after reading: ";
  240. Temp += strerror(errno);
  241. throw std::runtime_error(Temp);
  242. }
  243. if (!Explain()) {
  244. std::ofstream Output; // Write the updated contents.
  245. Output.open(PostfixMainCfPath.c_str(), std::ios::trunc);
  246. if (!Output) {
  247. std::string Temp;
  248. Temp = "Error opening the postfix configuration file " + PostfixMainCfPath;
  249. Temp += " for writing: ";
  250. Temp += strerror(errno);
  251. throw std::runtime_error(Temp);
  252. }
  253. Output << Content;
  254. if (!Output) {
  255. std::string Temp;
  256. Temp = "Error writing the postfix configuration file " + PostfixMainCfPath;
  257. Temp += ": ";
  258. Temp += strerror(errno);
  259. throw std::runtime_error(Temp);
  260. }
  261. Output.close();
  262. if (!Output) {
  263. std::string Temp;
  264. Temp = "Error closing the postfix configuration file " + PostfixMainCfPath;
  265. Temp += " after writing: ";
  266. Temp += strerror(errno);
  267. throw std::runtime_error(Temp);
  268. }
  269. }
  270. }
  271. OutputVerboseEnd();
  272. if (!ReloadMta()) {
  273. std::cerr << "Unable to reload the postfix configuration. Please run "
  274. << "'postfix reload' for the integration with SNFMilter to take effect.";
  275. }
  276. }
  277. bool
  278. PostfixIntegrate::MtaIsRunningDetected() {
  279. if (Verbose()) {
  280. std::cout << "Checking whether postfix is detected to be running...";
  281. }
  282. bool IsRunningDetected;
  283. IsRunningDetected = (std::system(MtaIsRunningCommand.c_str()) == 0);
  284. if (Verbose()) {
  285. std::cout << (IsRunningDetected ? "yes..." : "no...");
  286. }
  287. OutputVerboseEnd();
  288. return IsRunningDetected;
  289. }
  290. bool
  291. PostfixIntegrate::ReloadMta() {
  292. if (!MtaIsRunningDetected()) {
  293. return true;
  294. }
  295. if (Verbose()) {
  296. std::cout << "Reloading postfix...\n";
  297. std::cout.flush();
  298. }
  299. bool Succeeded;
  300. if (!Explain()) {
  301. Succeeded = (std::system(ReloadMtaCommand.c_str()) == 0);
  302. if (Verbose()) {
  303. std::cout << (Succeeded ? "succeeded..." : "failed...");
  304. }
  305. }
  306. OutputVerboseEnd();
  307. return Succeeded;
  308. }
  309. bool
  310. PostfixIntegrate::IsIntegrated() {
  311. if (Verbose()) {
  312. std::cout << "Checking for any SNFMilter integration in the postfix file " << PostfixMainCfPath << "...";
  313. }
  314. if (!FileExists(PostfixMainCfPath)) {
  315. if (Verbose()) {
  316. std::cout << "file doesn't exist; postfix is not integrated...";
  317. }
  318. OutputVerboseEnd();
  319. return false;
  320. }
  321. bool Integrated = false;
  322. std::ifstream Input;
  323. Input.open(PostfixMainCfPath.c_str()); // Read the contents.
  324. if (!Input) {
  325. std::string Temp;
  326. Temp = "Error opening the postfix configuration file " + PostfixMainCfPath;
  327. Temp += " for reading: ";
  328. Temp += strerror(errno);
  329. throw std::runtime_error(Temp);
  330. }
  331. PostfixMilterConf MilterConf(PostfixSocketSpec); // Object to update the config line.
  332. std::string Line;
  333. while (getline(Input, Line)) {
  334. MilterConf.ConfLine(Line);
  335. if (MilterConf.IsIntegrated()) { // Check for integration line.
  336. Integrated = true; // Found it.
  337. break;
  338. }
  339. }
  340. Input.close();
  341. if (Input.bad()) {
  342. std::string Temp;
  343. Temp = "Error closing the postfix configuration file " + PostfixMainCfPath;
  344. Temp += " after reading: ";
  345. Temp += strerror(errno);
  346. throw std::runtime_error(Temp);
  347. }
  348. if (Verbose()) {
  349. if (Integrated) {
  350. std::cout << "found '" << Line << "'...";
  351. } else {
  352. std::cout << "none found...";
  353. }
  354. }
  355. OutputVerboseEnd();
  356. return Integrated;
  357. }
  358. bool
  359. PostfixIntegrate::DefaultIsChrooted() {
  360. return PostfixDefaultIsChrooted;
  361. }
  362. bool
  363. PostfixIntegrate::MtaConfigurationIsChrooted() {
  364. std::string File;
  365. std::ifstream Input;
  366. File = PostfixMasterCfPath;
  367. Input.open(File.c_str());
  368. if (!Input) {
  369. std::string Temp;
  370. Temp = "Error opening postfix configuration file " + File;
  371. Temp += " for reading: ";
  372. Temp += strerror(errno);
  373. throw std::runtime_error(Temp);
  374. }
  375. std::string Line;
  376. bool ConfigurationIsChrooted = false;
  377. while (getline(Input, Line)) {
  378. if (CheckForString(Line, "smtp")) { // Check for smtp line.
  379. std::istringstream Buffer(Line); // Parse buffer line.
  380. std::string Token[8];
  381. for (unsigned int iToken = 0; iToken < 8; iToken++) {
  382. Buffer >> Token[iToken];
  383. }
  384. if ( ("y" == Token[4]) || ("-" == Token[4]) ) {
  385. Input.close();
  386. if (Input.bad()) {
  387. std::string Temp;
  388. Temp = "Error closing the postfix configuration file " + File;
  389. Temp += " after reading: ";
  390. Temp += strerror(errno);
  391. throw std::runtime_error(Temp);
  392. }
  393. return true;
  394. }
  395. }
  396. }
  397. Input.close();
  398. if (Input.bad()) {
  399. std::string Temp;
  400. Temp = "Error closing the rulebase download script file " + File;
  401. Temp += " after reading: ";
  402. Temp += strerror(errno);
  403. throw std::runtime_error(Temp);
  404. }
  405. return false;
  406. }