選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

postsrsd.c 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /* PostSRSd - Sender Rewriting Scheme daemon for Postfix
  2. * Copyright (c) 2012 Timo Röhling <timo.roehling@gmx.de>
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. /* This program uses the libsrs2 library. The relevant source
  18. * files have been added to this distribution. */
  19. #include "srs2.h"
  20. #include <sys/types.h>
  21. #include <sys/socket.h>
  22. #include <netdb.h>
  23. #include <errno.h>
  24. #include <unistd.h>
  25. #include <fcntl.h>
  26. #include <pwd.h>
  27. #include <string.h>
  28. #include <poll.h>
  29. #include <wait.h>
  30. #include <syslog.h>
  31. #ifndef VERSION
  32. #define VERSION "1.0"
  33. #endif
  34. static char *self = NULL;
  35. static int bind_service (const char *service, int family)
  36. {
  37. struct addrinfo *addr, *it;
  38. struct addrinfo hints;
  39. int err, sock, flags;
  40. static const int one = 1;
  41. memset (&hints, 0, sizeof(hints));
  42. hints.ai_family = family;
  43. hints.ai_socktype = SOCK_STREAM;
  44. hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
  45. err = getaddrinfo(NULL, service, &hints, &addr);
  46. if (err != 0) {
  47. fprintf(stderr, "%s: bind_service(%s): %s\n", self, service, gai_strerror(err));
  48. return -1;
  49. }
  50. sock = -1;
  51. for (it = addr; it; it = it->ai_next) {
  52. sock = socket(it->ai_family, it->ai_socktype, it->ai_protocol);
  53. if (sock < 0) goto fail;
  54. if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) goto fail;
  55. if (bind(sock, it->ai_addr, it->ai_addrlen) < 0) goto fail;
  56. if (listen(sock, 10) < 0) goto fail;
  57. flags = fcntl (sock, F_GETFL, 0);
  58. if (flags < 0) goto fail;
  59. if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) goto fail;
  60. break;
  61. fail:
  62. err = errno;
  63. if (sock >= 0) close (sock);
  64. sock = -1;
  65. }
  66. freeaddrinfo (addr);
  67. if (sock < 0)
  68. fprintf (stderr, "%s: bind_service(%s): %s\n", self, service, strerror(err));
  69. return sock;
  70. }
  71. static int is_hexdigit (char c)
  72. {
  73. return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
  74. }
  75. static char hex2num (char c)
  76. {
  77. if (c >= '0' && c <= '9') return c - '0';
  78. if (c >= 'a' && c <= 'f') return c - 'a' + 10;
  79. if (c >= 'A' && c <= 'F') return c - 'A' + 10;
  80. }
  81. static char num2hex (char c)
  82. {
  83. if (c < 10) return '0' + c;
  84. return 'a' + c - 10;
  85. }
  86. static char hex2char (const char *s)
  87. {
  88. return 16 * hex2num(s[0]) + hex2num(s[1]);
  89. }
  90. static void char2hex (char c, char *buf)
  91. {
  92. buf[0] = num2hex((c >> 4) & 0xf);
  93. buf[1] = num2hex((c ) & 0xf);
  94. }
  95. static char* url_decode (char *buf, size_t len, const char *input)
  96. {
  97. char *output = buf;
  98. if (!input || !output || len == 0) return NULL;
  99. while (*input && --len) {
  100. if (*input == '%' && is_hexdigit(input[1]) && is_hexdigit(input[2])) {
  101. *output++ = hex2char(++input);
  102. input += 2;
  103. } else {
  104. *output++ = *input++;
  105. }
  106. }
  107. *output = 0;
  108. return buf;
  109. }
  110. static char* url_encode (char* buf, size_t len, const char *input)
  111. {
  112. char *output = buf;
  113. if (!input || !output || len == 0) return NULL;
  114. while (*input && --len) {
  115. if (!isascii(*input) || !isgraph(*input) || *input == '%') {
  116. if (len <= 2) break;
  117. *output++ = '%';
  118. char2hex(*input++, output);
  119. output += 2; len -= 2;
  120. } else {
  121. *output++ = *input++;
  122. }
  123. }
  124. *output = 0;
  125. return buf;
  126. }
  127. static void handle_forward (srs_t *srs, FILE *fp, const char *address, const char *domain)
  128. {
  129. int result;
  130. char value[1024];
  131. char outputbuf[1024], *output;
  132. result = srs_forward(srs, value, sizeof(value), address, domain);
  133. if (result == SRS_SUCCESS) {
  134. output = url_encode(outputbuf, sizeof(outputbuf), value);
  135. fprintf (fp, "200 %s\n", output);
  136. if (strcmp(address, value) != 0) syslog (LOG_MAIL | LOG_INFO, "srs_forward: <%s> rewritten as <%s>", address, value);
  137. } else {
  138. fprintf (fp, "500 %s\n", srs_strerror(result));
  139. if (result != SRS_ENOTREWRITTEN)
  140. syslog (LOG_MAIL | LOG_INFO, "srs_forward: <%s> not rewritten: %s", address, srs_strerror(result));
  141. }
  142. fflush (fp);
  143. }
  144. static void handle_reverse (srs_t *srs, FILE *fp, const char *address, const char *domain)
  145. {
  146. int result;
  147. char value[1024];
  148. char outputbuf[1024], *output;
  149. result = srs_reverse(srs, value, sizeof(value), address);
  150. if (result == SRS_SUCCESS) {
  151. output = url_encode(outputbuf, sizeof(outputbuf), value);
  152. fprintf (fp, "200 %s\n", output);
  153. syslog (LOG_MAIL | LOG_INFO, "srs_reverse: <%s> rewritten as <%s>", address, value);
  154. } else {
  155. fprintf (fp, "500 %s\n", srs_strerror(result));
  156. if (result != SRS_ENOTREWRITTEN && result != SRS_ENOTSRSADDRESS)
  157. syslog (LOG_MAIL | LOG_INFO, "srs_reverse: <%s> not rewritten: %s", address, srs_strerror(result));
  158. }
  159. fflush (fp);
  160. }
  161. static void show_help ()
  162. {
  163. fprintf (stdout,
  164. "Sender Rewriting Scheme implementation for Postfix.\n\n"
  165. "Implements two TCP lookup tables to rewrite mail addresses\n"
  166. "as needed. The forward SRS is for sender envelope addresses\n"
  167. "to prevent SPF-related bounces. The reverse SRS is for\n"
  168. "recipient envelope addresses so that bounced mails can be\n"
  169. "routed back to their original sender.\n"
  170. "\n"
  171. "Usage: %s -s<file> -d<domain> [other options]\n"
  172. "Options:\n"
  173. " -s<file> read secret from file (required)\n"
  174. " -d<domain> set domain name for rewrite (required)\n"
  175. " -f<port> set port for the forward SRS lookup (default: 10001)\n"
  176. " -r<port> set port for the reverse SRS lookup (default: 10002)\n"
  177. " -p<pidfile> write process ID to pidfile (default: none)\n"
  178. " -c<dir> chroot to <dir> (default: none)\n"
  179. " -u<user> switch user id after port bind (default: none)\n"
  180. " -t<seconds> timeout for idle client connections (default: 1800)\n"
  181. " -D fork into background\n"
  182. " -4 force IPv4 socket (default: any)\n"
  183. " -6 force IPv6 socket (default: any)\n"
  184. " -h show this help\n"
  185. " -v show version\n"
  186. "\n",
  187. self
  188. );
  189. }
  190. typedef void(*handle_t)(srs_t*, FILE*, const char*, const char*);
  191. int main (int argc, char **argv)
  192. {
  193. int opt, timeout = 1800, family = AF_UNSPEC;
  194. int daemonize = FALSE;
  195. char *forward_service = NULL, *reverse_service = NULL,
  196. *user = NULL, *domain = NULL, *chroot_dir = NULL;
  197. int forward_sock, reverse_sock;
  198. char *secret_file = NULL, *pid_file = NULL;
  199. FILE *pf = NULL;
  200. struct passwd *pwd = NULL;
  201. char secret[1024];
  202. char *tmp;
  203. srs_t *srs;
  204. struct pollfd fds[3];
  205. handle_t handler[2] = { handle_forward, handle_reverse };
  206. tmp = strrchr(argv[0], '/');
  207. if (tmp) self = strdup(tmp + 1); else self = strdup(argv[0]);
  208. while ((opt = getopt(argc, argv, "46d:f:r:s:u:t:p:c:Dhv")) != -1) {
  209. switch (opt) {
  210. case '?':
  211. return EXIT_FAILURE;
  212. case '4':
  213. family = AF_INET;
  214. break;
  215. case '6':
  216. family = AF_INET6;
  217. break;
  218. case 'd':
  219. domain = strdup(optarg);
  220. break;
  221. case 'f':
  222. forward_service = strdup(optarg);
  223. break;
  224. case 'r':
  225. reverse_service = strdup(optarg);
  226. break;
  227. case 't':
  228. timeout = atoi(optarg);
  229. break;
  230. case 's':
  231. secret_file = strdup(optarg);
  232. break;
  233. case 'p':
  234. pid_file = strdup(optarg);
  235. break;
  236. case 'u':
  237. user = strdup(optarg);
  238. break;
  239. case 'c':
  240. chroot_dir = strdup(optarg);
  241. break;
  242. case 'D':
  243. daemonize = TRUE;
  244. break;
  245. case 'h':
  246. show_help();
  247. return EXIT_SUCCESS;
  248. case 'v':
  249. fprintf (stdout, "%s\n", VERSION);
  250. return EXIT_SUCCESS;
  251. }
  252. }
  253. if (domain == NULL) {
  254. fprintf (stderr, "%s: You must set a home domain (-d)\n", self);
  255. show_help();
  256. return EXIT_FAILURE;
  257. }
  258. /* The stuff we do first may not be possible from within chroot or without privileges */
  259. /* Open pid file for writing (the actual process ID is filled in later) */
  260. if (pid_file) {
  261. pf = fopen (pid_file, "w");
  262. if (pf == NULL) {
  263. fprintf (stderr, "%s: Cannot write PID: %s\n\n", self, pid_file);
  264. return EXIT_FAILURE;
  265. }
  266. }
  267. /* Read secret. The default installation makes this root accessible only. */
  268. if (secret_file != NULL) {
  269. size_t len;
  270. FILE *fp = fopen(secret_file, "rb");
  271. if (fp == NULL) {
  272. fprintf (stderr, "%s: Cannot open file with secret: %s\n", self, secret_file);
  273. return EXIT_FAILURE;
  274. }
  275. len = fread(secret, 1, sizeof(secret) - 1, fp);
  276. if (len == 0 || len > sizeof(secret) - 1) {
  277. fprintf (stderr, "%s: Cannot read secret from file: %s\n", self, secret_file);
  278. return EXIT_FAILURE;
  279. }
  280. secret[len] = 0;
  281. fclose (fp);
  282. } else {
  283. fprintf (stderr, "%s: You must set a secret (-s)\n", self);
  284. show_help();
  285. return EXIT_FAILURE;
  286. }
  287. /* Bind ports. May require privileges if the config specifies ports below 1024 */
  288. if (forward_service != NULL) {
  289. forward_sock = bind_service(forward_service, family);
  290. free (forward_service);
  291. } else {
  292. forward_sock = bind_service("10001", family);
  293. }
  294. if (forward_sock < 0) return EXIT_FAILURE;
  295. if (reverse_service != NULL) {
  296. reverse_sock = bind_service(reverse_service, family);
  297. free (reverse_service);
  298. } else {
  299. reverse_sock = bind_service("10002", family);
  300. }
  301. if (reverse_sock < 0) return EXIT_FAILURE;
  302. /* Open syslog now (NDELAY), because it may no longer reachable from chroot */
  303. openlog (self, LOG_PID | LOG_NDELAY, LOG_MAIL);
  304. /* We also have to lookup the uid of the unprivileged user for the same reason. */
  305. if (user) {
  306. errno = 0;
  307. pwd = getpwnam(user);
  308. if (pwd == NULL) {
  309. if (errno != 0)
  310. fprintf (stderr, "%s: Failed to lookup user: %s\n", self, strerror(errno));
  311. else
  312. fprintf (stderr, "%s: No such user: %s\n", self, user);
  313. return EXIT_FAILURE;
  314. }
  315. }
  316. /* Now we can chroot, which again requires root privileges */
  317. if (chroot_dir) {
  318. if (chdir(chroot_dir) < 0) {
  319. fprintf (stderr, "%s: Cannot change to chroot: %s\n", self, strerror(errno));
  320. return EXIT_FAILURE;
  321. }
  322. if (chroot(chroot_dir) < 0) {
  323. fprintf (stderr, "%s: Failed to enable chroot: %s\n", self, strerror(errno));
  324. return EXIT_FAILURE;
  325. }
  326. }
  327. /* Finally, we revert to the unprivileged user */
  328. if (pwd) {
  329. if (setuid(pwd->pw_uid) < 0) {
  330. fprintf (stderr, "%s: Failed to switch user id: %s\n", self, strerror(errno));
  331. return EXIT_FAILURE;
  332. }
  333. }
  334. /* Standard double fork technique to disavow all knowledge about the controlling terminal */
  335. if (daemonize) {
  336. close(0); close(1); close(2);
  337. if (fork() != 0) return EXIT_SUCCESS;
  338. setsid();
  339. if (fork() != 0) return EXIT_SUCCESS;
  340. }
  341. /* Make note of our actual process ID */
  342. if (pf) {
  343. fprintf (pf, "%d", (int)getpid());
  344. fclose (pf);
  345. }
  346. srs = srs_new();
  347. srs_add_secret (srs, secret);
  348. srs_set_separator (srs, '+');
  349. fds[0].fd = forward_sock;
  350. fds[0].events = POLLIN;
  351. fds[1].fd = reverse_sock;
  352. fds[1].events = POLLIN;
  353. while(TRUE) {
  354. int i, conn;
  355. FILE *fp;
  356. char linebuf[1024], *line;
  357. char keybuf[1024], *key;
  358. if (poll(fds, 2, 1000) < 0) {
  359. if (daemonize)
  360. syslog (LOG_MAIL | LOG_ERR, "Poll failure: %s", strerror(errno));
  361. else
  362. fprintf (stderr, "%s: Poll failure: %s\n", self, strerror(errno));
  363. return EXIT_FAILURE;
  364. }
  365. for (i = 0; i < 2; ++i) {
  366. if (fds[i].revents) {
  367. conn = accept(fds[i].fd, NULL, NULL);
  368. if (conn < 0) continue;
  369. if (fork() == 0) {
  370. fp = fdopen(conn, "r+");
  371. if (fp == NULL) exit(EXIT_FAILURE);
  372. fds[2].fd = conn;
  373. fds[2].events = POLLIN;
  374. if (poll(&fds[2], 1, timeout * 1000) <= 0) return EXIT_FAILURE;
  375. line = fgets(linebuf, sizeof(linebuf), fp);
  376. while (line) {
  377. char* token;
  378. token = strtok(line, " \r\n");
  379. if (token == NULL || strcmp(token, "get") != 0) {
  380. fprintf (fp, "500 Invalid request\n");
  381. return EXIT_FAILURE;
  382. }
  383. token = strtok(NULL, "\r\n");
  384. if (!token) {
  385. fprintf (fp, "500 Invalid request\n");
  386. return EXIT_FAILURE;
  387. }
  388. key = url_decode(keybuf, sizeof(keybuf), token);
  389. if (!key) break;
  390. handler[i](srs, fp, key, domain);
  391. if (poll(&fds[2], 1, timeout * 1000) <= 0) break;
  392. line = fgets(linebuf, sizeof(linebuf), fp);
  393. }
  394. fclose (fp);
  395. return EXIT_SUCCESS;
  396. }
  397. close (conn);
  398. }
  399. }
  400. waitpid(-1, NULL, WNOHANG);
  401. }
  402. return EXIT_SUCCESS;
  403. }