// // Created by robin on 8/8/15. // #include #include #include #include #include #include "CommandLineParser.h" #include "MainClass.h" MainClass* MainClass::_instance = nullptr; MainClass *MainClass::getInstance() { return _instance; } sfsistat mlfi_header(SMFICTX *ctx) { return MainClass::getInstance()->mlfiHeader(ctx); } MainClass::MainClass(int argc, char *argv[]) : _argc(argc) , _argv(argv) { _instance = this; _cryptoBusiness = std::make_shared(); } MainClass::~MainClass() { } int MainClass::main() { CommandLineParser commandLineParser(_argc, _argv); CommandLineOption configOption("config", 'c', "Use FILE as configuration file.", "FILE", "/etc/milter-sasl/milter-sasl.json"); commandLineParser.addOption(&configOption); CommandLineOption decryptOption("decrypt", 'd', "Decrypt DATA and exit. Can be specified multiple times. Use - to read from stdin.", "DATA"); commandLineParser.addOption(&decryptOption); CommandLineOption helpOption("help", 'h', "Show this help."); commandLineParser.addOption(&helpOption); if (!commandLineParser.parse()) { return commandLineParser.showHelp(EX_USAGE, true); } if (helpOption.isSet()) { return commandLineParser.showHelp(0, false); } auto configFile = configOption.getValue(); auto configResult = loadConfig(configFile); if (configResult != 0) { std::cerr << "Failed to read configuration file: " << strerror(configResult) << std::endl; return EX_NOINPUT; } else if (decryptOption.isSet()) { return launchDecrypt(decryptOption.getValues()); } else { return launchMilter(_defaultSocket); } } sfsistat MainClass::mlfiHeader(SMFICTX *ctx) { Json::Value root; auto auth_authen = smfi_getsymval(ctx, (char*)"{auth_authen}"); if (auth_authen) { root["auth_authen"] = randomizeString(auth_authen); } auto auth_author = smfi_getsymval(ctx, (char*)"{auth_author}"); if (auth_author) { root["auth_author"] = randomizeString(auth_author); } auto auth_type = smfi_getsymval(ctx, (char*)"{auth_type}"); if (auth_type) { root["auth_type"] = randomizeString(auth_type); } Json::StyledWriter w; auto json = w.write(root); auto encrypted = _cryptoBusiness->encryptToHex(json); smfi_addheader(ctx, (char*)"X-Sasl-User", (char*)encrypted.c_str()); return SMFIS_CONTINUE; } int MainClass::loadConfig(const std::string& filePath) { std::ifstream stream(filePath, std::ifstream::in); if (stream) { Json::Reader reader; Json::Value root; reader.parse(stream, root); _cryptoBusiness->setKey(root["key"].asString()); _defaultSocket = root["socket"].asString(); } return errno; } int MainClass::launchMilter(const std::string &socket) { srand(time(nullptr)); struct smfiDesc smfilter = { (char*)"milter-sasl", /* filter name */ SMFI_VERSION, /* version code -- do not change */ SMFIF_ADDHDRS, /* flags */ NULL, /* connection info filter */ NULL, /* SMTP HELO command filter */ NULL, /* envelope sender filter */ NULL, /* envelope recipient filter */ NULL, /* header filter */ NULL, /* end of header */ NULL, /* body block filter */ mlfi_header, /* end of message */ NULL, /* message aborted */ NULL, /* connection cleanup */ NULL, /* unknown/unimplemented SMTP commands */ NULL, /* DATA command filter */ NULL /* option negotiation at connection startup */ }; if (smfi_setconn((char*)socket.c_str()) == MI_FAILURE) { std::cerr << "smfi_setconn failed" << std::endl; return EX_UNAVAILABLE; } if (smfi_register(smfilter) == MI_FAILURE) { std::cerr << "smfi_register failed" << std::endl; return EX_UNAVAILABLE; } return smfi_main(); } int MainClass::launchDecrypt(const std::vector values) { for (auto value : values) { if (value == "-") { std::cin >> value; } std::cout << _cryptoBusiness->decryptFromHex(value) << std::endl; } return 0; } std::string MainClass::randomizeString(const std::string &str) { int cc = 2; std::string out; for (int i = 0; i < cc; ++i) { auto c = 'a' + (rand() % 26); out.push_back((char)c); } out += "|" + str + "|"; for (int i = 0; i < cc; ++i) { auto c = 'a' + (rand() % 26); out.push_back((char)c); } return out; }