Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

postsrsd.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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. #ifdef HAVE_SYS_TIME_H
  30. #include <sys/time.h>
  31. #endif
  32. #ifdef HAVE_TIME_H
  33. #include <time.h>
  34. #endif
  35. #ifdef HAVE_SYS_WAIT_H
  36. #include <sys/wait.h>
  37. #endif
  38. #ifdef HAVE_WAIT_H
  39. #include <wait.h>
  40. #endif
  41. #include <syslog.h>
  42. #ifndef VERSION
  43. #define VERSION "1.1"
  44. #endif
  45. static char *self = NULL;
  46. static int bind_service (const char *service, int family)
  47. {
  48. struct addrinfo *addr, *it;
  49. struct addrinfo hints;
  50. int err, sock, flags;
  51. static const int one = 1;
  52. memset (&hints, 0, sizeof(hints));
  53. hints.ai_family = family;
  54. hints.ai_socktype = SOCK_STREAM;
  55. hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
  56. err = getaddrinfo(NULL, service, &hints, &addr);
  57. if (err != 0) {
  58. fprintf(stderr, "%s: bind_service(%s): %s\n", self, service, gai_strerror(err));
  59. return -1;
  60. }
  61. sock = -1;
  62. for (it = addr; it; it = it->ai_next) {
  63. sock = socket(it->ai_family, it->ai_socktype, it->ai_protocol);
  64. if (sock < 0) goto fail;
  65. if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) goto fail;
  66. if (bind(sock, it->ai_addr, it->ai_addrlen) < 0) goto fail;
  67. if (listen(sock, 10) < 0) goto fail;
  68. flags = fcntl (sock, F_GETFL, 0);
  69. if (flags < 0) goto fail;
  70. if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) goto fail;
  71. break;
  72. fail:
  73. err = errno;
  74. if (sock >= 0) close (sock);
  75. sock = -1;
  76. }
  77. freeaddrinfo (addr);
  78. if (sock < 0)
  79. fprintf (stderr, "%s: bind_service(%s): %s\n", self, service, strerror(err));
  80. return sock;
  81. }
  82. static int is_hexdigit (char c)
  83. {
  84. return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
  85. }
  86. static char hex2num (char c)
  87. {
  88. if (c >= '0' && c <= '9') return c - '0';
  89. if (c >= 'a' && c <= 'f') return c - 'a' + 10;
  90. if (c >= 'A' && c <= 'F') return c - 'A' + 10;
  91. return 0;
  92. }
  93. static char num2hex (char c)
  94. {
  95. if (c < 10) return '0' + c;
  96. return 'a' + c - 10;
  97. }
  98. static char hex2char (const char *s)
  99. {
  100. return 16 * hex2num(s[0]) + hex2num(s[1]);
  101. }
  102. static void char2hex (char c, char *buf)
  103. {
  104. buf[0] = num2hex((c >> 4) & 0xf);
  105. buf[1] = num2hex((c ) & 0xf);
  106. }
  107. static char* url_decode (char *buf, size_t len, const char *input)
  108. {
  109. char *output = buf;
  110. if (!input || !output || len == 0) return NULL;
  111. while (*input && --len) {
  112. if (*input == '%' && is_hexdigit(input[1]) && is_hexdigit(input[2])) {
  113. *output++ = hex2char(++input);
  114. input += 2;
  115. } else {
  116. *output++ = *input++;
  117. }
  118. }
  119. *output = 0;
  120. return buf;
  121. }
  122. static char* url_encode (char* buf, size_t len, const char *input)
  123. {
  124. char *output = buf;
  125. if (!input || !output || len == 0) return NULL;
  126. while (*input && --len) {
  127. if (!isascii(*input) || !isgraph(*input) || *input == '%') {
  128. if (len <= 2) break;
  129. *output++ = '%';
  130. char2hex(*input++, output);
  131. output += 2; len -= 2;
  132. } else {
  133. *output++ = *input++;
  134. }
  135. }
  136. *output = 0;
  137. return buf;
  138. }
  139. static void handle_forward (srs_t *srs, FILE *fp, const char *address, const char *domain, const char **excludes)
  140. {
  141. int result;
  142. size_t addrlen;
  143. char value[1024];
  144. char outputbuf[1024], *output;
  145. addrlen = strlen(address);
  146. for(; *excludes; excludes++) {
  147. size_t len;
  148. len = strlen(*excludes);
  149. if (len >= addrlen) continue;
  150. if (strcasecmp(*excludes, &address[addrlen - len]) == 0 && (**excludes == '.' || address[addrlen - len - 1] == '@')) {
  151. fputs ("500 Domain excluded from SRS\n", fp);
  152. fflush (fp);
  153. return;
  154. }
  155. }
  156. result = srs_forward(srs, value, sizeof(value), address, domain);
  157. if (result == SRS_SUCCESS) {
  158. output = url_encode(outputbuf, sizeof(outputbuf), value);
  159. fprintf (fp, "200 %s\n", output);
  160. if (strcmp(address, value) != 0) syslog (LOG_MAIL | LOG_INFO, "srs_forward: <%s> rewritten as <%s>", address, value);
  161. } else {
  162. fprintf (fp, "500 %s\n", srs_strerror(result));
  163. if (result != SRS_ENOTREWRITTEN)
  164. syslog (LOG_MAIL | LOG_INFO, "srs_forward: <%s> not rewritten: %s", address, srs_strerror(result));
  165. }
  166. fflush (fp);
  167. }
  168. static void handle_reverse (srs_t *srs, FILE *fp, const char *address, const char *domain, const char **excludes)
  169. {
  170. int result;
  171. char value[1024];
  172. char outputbuf[1024], *output;
  173. result = srs_reverse(srs, value, sizeof(value), address);
  174. if (result == SRS_SUCCESS) {
  175. output = url_encode(outputbuf, sizeof(outputbuf), value);
  176. fprintf (fp, "200 %s\n", output);
  177. syslog (LOG_MAIL | LOG_INFO, "srs_reverse: <%s> rewritten as <%s>", address, value);
  178. } else {
  179. fprintf (fp, "500 %s\n", srs_strerror(result));
  180. if (result != SRS_ENOTREWRITTEN && result != SRS_ENOTSRSADDRESS)
  181. syslog (LOG_MAIL | LOG_INFO, "srs_reverse: <%s> not rewritten: %s", address, srs_strerror(result));
  182. }
  183. fflush (fp);
  184. }
  185. static void show_help ()
  186. {
  187. fprintf (stdout,
  188. "Sender Rewriting Scheme implementation for Postfix.\n\n"
  189. "Implements two TCP lookup tables to rewrite mail addresses\n"
  190. "as needed. The forward SRS is for sender envelope addresses\n"
  191. "to prevent SPF-related bounces. The reverse SRS is for\n"
  192. "recipient envelope addresses so that bounced mails can be\n"
  193. "routed back to their original sender.\n"
  194. "\n"
  195. "Usage: %s -s<file> -d<domain> [other options]\n"
  196. "Options:\n"
  197. " -s<file> read secrets from file (required)\n"
  198. " -d<domain> set domain name for rewrite (required)\n"
  199. " -f<port> set port for the forward SRS lookup (default: 10001)\n"
  200. " -r<port> set port for the reverse SRS lookup (default: 10002)\n"
  201. " -p<pidfile> write process ID to pidfile (default: none)\n"
  202. " -c<dir> chroot to <dir> (default: none)\n"
  203. " -u<user> switch user id after port bind (default: none)\n"
  204. " -t<seconds> timeout for idle client connections (default: 1800)\n"
  205. " -X<domain> exclude additional domain from address rewriting\n"
  206. " -D fork into background\n"
  207. " -4 force IPv4 socket (default: any)\n"
  208. " -6 force IPv6 socket (default: any)\n"
  209. " -h show this help\n"
  210. " -v show version\n"
  211. "\n",
  212. self
  213. );
  214. }
  215. typedef void(*handle_t)(srs_t*, FILE*, const char*, const char*, const char**);
  216. int main (int argc, char **argv)
  217. {
  218. int opt, timeout = 1800, family = AF_UNSPEC;
  219. int daemonize = FALSE;
  220. char *forward_service = NULL, *reverse_service = NULL,
  221. *user = NULL, *domain = NULL, *chroot_dir = NULL;
  222. int forward_sock, reverse_sock;
  223. char *secret_file = NULL, *pid_file = NULL;
  224. FILE *pf = NULL, *sf = NULL;
  225. struct passwd *pwd = NULL;
  226. char secretbuf[1024], *secret = NULL;
  227. char *tmp;
  228. time_t now;
  229. srs_t *srs;
  230. struct pollfd fds[3];
  231. const char **excludes;
  232. size_t s1 = 0, s2 = 1;
  233. handle_t handler[2] = { handle_forward, handle_reverse };
  234. excludes = (const char**)calloc(1, sizeof(char*));
  235. tmp = strrchr(argv[0], '/');
  236. if (tmp) self = strdup(tmp + 1); else self = strdup(argv[0]);
  237. while ((opt = getopt(argc, argv, "46d:f:r:s:u:t:p:c:X::Dhv")) != -1) {
  238. switch (opt) {
  239. case '?':
  240. return EXIT_FAILURE;
  241. case '4':
  242. family = AF_INET;
  243. break;
  244. case '6':
  245. family = AF_INET6;
  246. break;
  247. case 'd':
  248. domain = strdup(optarg);
  249. break;
  250. case 'f':
  251. forward_service = strdup(optarg);
  252. break;
  253. case 'r':
  254. reverse_service = strdup(optarg);
  255. break;
  256. case 't':
  257. timeout = atoi(optarg);
  258. break;
  259. case 's':
  260. secret_file = strdup(optarg);
  261. break;
  262. case 'p':
  263. pid_file = strdup(optarg);
  264. break;
  265. case 'u':
  266. user = strdup(optarg);
  267. break;
  268. case 'c':
  269. chroot_dir = strdup(optarg);
  270. break;
  271. case 'D':
  272. daemonize = TRUE;
  273. break;
  274. case 'h':
  275. show_help();
  276. return EXIT_SUCCESS;
  277. case 'X':
  278. if (optarg != NULL) {
  279. tmp = strtok(optarg, ",; \t\r\n");
  280. while (tmp) {
  281. if (s1 + 1 >= s2) {
  282. s2 *= 2;
  283. excludes = (const char **)realloc(excludes, s2 * sizeof(char*));
  284. if (excludes == NULL) {
  285. fprintf (stderr, "%s: Out of memory\n\n", self);
  286. return EXIT_FAILURE;
  287. }
  288. }
  289. excludes[s1++] = strdup(tmp);
  290. tmp = strtok(NULL, ",; \t\r\n");
  291. }
  292. excludes[s1] = NULL;
  293. }
  294. break;
  295. case 'v':
  296. fprintf (stdout, "%s\n", VERSION);
  297. return EXIT_SUCCESS;
  298. }
  299. }
  300. if (optind < argc) {
  301. fprintf (stderr, "%s: extra argument on command line: %s\n", self, argv[optind]);
  302. return EXIT_FAILURE;
  303. }
  304. if (domain == NULL) {
  305. fprintf (stderr, "%s: You must set a home domain (-d)\n", self);
  306. return EXIT_FAILURE;
  307. }
  308. /* The stuff we do first may not be possible from within chroot or without privileges */
  309. /* Open pid file for writing (the actual process ID is filled in later) */
  310. if (pid_file) {
  311. pf = fopen (pid_file, "w");
  312. if (pf == NULL) {
  313. fprintf (stderr, "%s: Cannot write PID: %s\n\n", self, pid_file);
  314. return EXIT_FAILURE;
  315. }
  316. }
  317. /* Read secret. The default installation makes this root accessible only. */
  318. if (secret_file != NULL) {
  319. size_t len;
  320. sf = fopen(secret_file, "rb");
  321. if (sf == NULL) {
  322. fprintf (stderr, "%s: Cannot open file with secret: %s\n", self, secret_file);
  323. return EXIT_FAILURE;
  324. }
  325. } else {
  326. fprintf (stderr, "%s: You must set a secret (-s)\n", self);
  327. return EXIT_FAILURE;
  328. }
  329. /* Bind ports. May require privileges if the config specifies ports below 1024 */
  330. if (forward_service != NULL) {
  331. forward_sock = bind_service(forward_service, family);
  332. free (forward_service);
  333. } else {
  334. forward_sock = bind_service("10001", family);
  335. }
  336. if (forward_sock < 0) return EXIT_FAILURE;
  337. if (reverse_service != NULL) {
  338. reverse_sock = bind_service(reverse_service, family);
  339. free (reverse_service);
  340. } else {
  341. reverse_sock = bind_service("10002", family);
  342. }
  343. if (reverse_sock < 0) return EXIT_FAILURE;
  344. /* Open syslog now (NDELAY), because it may no longer reachable from chroot */
  345. openlog (self, LOG_PID | LOG_NDELAY, LOG_MAIL);
  346. /* Force loading of timezone info (suggested by patrickdk77) */
  347. now = time(NULL);
  348. localtime (&now);
  349. /* We also have to lookup the uid of the unprivileged user for the same reason. */
  350. if (user) {
  351. errno = 0;
  352. pwd = getpwnam(user);
  353. if (pwd == NULL) {
  354. if (errno != 0)
  355. fprintf (stderr, "%s: Failed to lookup user: %s\n", self, strerror(errno));
  356. else
  357. fprintf (stderr, "%s: No such user: %s\n", self, user);
  358. return EXIT_FAILURE;
  359. }
  360. }
  361. /* Now we can chroot, which again requires root privileges */
  362. if (chroot_dir) {
  363. if (chdir(chroot_dir) < 0) {
  364. fprintf (stderr, "%s: Cannot change to chroot: %s\n", self, strerror(errno));
  365. return EXIT_FAILURE;
  366. }
  367. if (chroot(chroot_dir) < 0) {
  368. fprintf (stderr, "%s: Failed to enable chroot: %s\n", self, strerror(errno));
  369. return EXIT_FAILURE;
  370. }
  371. }
  372. /* Finally, we revert to the unprivileged user */
  373. if (pwd) {
  374. if (setuid(pwd->pw_uid) < 0) {
  375. fprintf (stderr, "%s: Failed to switch user id: %s\n", self, strerror(errno));
  376. return EXIT_FAILURE;
  377. }
  378. }
  379. /* Standard double fork technique to disavow all knowledge about the controlling terminal */
  380. if (daemonize) {
  381. close(0); close(1); close(2);
  382. if (fork() != 0) return EXIT_SUCCESS;
  383. setsid();
  384. if (fork() != 0) return EXIT_SUCCESS;
  385. }
  386. /* Make note of our actual process ID */
  387. if (pf) {
  388. fprintf (pf, "%d", (int)getpid());
  389. fclose (pf);
  390. }
  391. srs = srs_new();
  392. while ((secret = fgets(secretbuf, sizeof(secretbuf), sf))) {
  393. secret = strtok(secret, "\r\n");
  394. if (secret)
  395. srs_add_secret (srs, secret);
  396. }
  397. fclose (sf);
  398. srs_set_separator (srs, '+');
  399. fds[0].fd = forward_sock;
  400. fds[0].events = POLLIN;
  401. fds[1].fd = reverse_sock;
  402. fds[1].events = POLLIN;
  403. while(TRUE) {
  404. int i, conn;
  405. FILE *fp;
  406. char linebuf[1024], *line;
  407. char keybuf[1024], *key;
  408. if (poll(fds, 2, 1000) < 0) {
  409. if (errno == EINTR)
  410. continue;
  411. if (daemonize)
  412. syslog (LOG_MAIL | LOG_ERR, "Poll failure: %s", strerror(errno));
  413. else
  414. fprintf (stderr, "%s: Poll failure: %s\n", self, strerror(errno));
  415. return EXIT_FAILURE;
  416. }
  417. for (i = 0; i < 2; ++i) {
  418. if (fds[i].revents) {
  419. conn = accept(fds[i].fd, NULL, NULL);
  420. if (conn < 0) continue;
  421. if (fork() == 0) {
  422. // close listen sockets so that we don't stop the main daemon process from restarting
  423. close(forward_sock);
  424. close(reverse_sock);
  425. fp = fdopen(conn, "r+");
  426. if (fp == NULL) exit(EXIT_FAILURE);
  427. fds[2].fd = conn;
  428. fds[2].events = POLLIN;
  429. if (poll(&fds[2], 1, timeout * 1000) <= 0) return EXIT_FAILURE;
  430. line = fgets(linebuf, sizeof(linebuf), fp);
  431. while (line) {
  432. fseek (fp, 0, SEEK_CUR); /* Workaround for Solaris */
  433. char* token;
  434. token = strtok(line, " \r\n");
  435. if (token == NULL || strcmp(token, "get") != 0) {
  436. fprintf (fp, "500 Invalid request\n");
  437. fflush (fp);
  438. return EXIT_FAILURE;
  439. }
  440. token = strtok(NULL, "\r\n");
  441. if (!token) {
  442. fprintf (fp, "500 Invalid request\n");
  443. fflush (fp);
  444. return EXIT_FAILURE;
  445. }
  446. key = url_decode(keybuf, sizeof(keybuf), token);
  447. if (!key) break;
  448. handler[i](srs, fp, key, domain, excludes);
  449. fflush (fp);
  450. if (poll(&fds[2], 1, timeout * 1000) <= 0) break;
  451. line = fgets(linebuf, sizeof(linebuf), fp);
  452. }
  453. fclose (fp);
  454. return EXIT_SUCCESS;
  455. }
  456. close (conn);
  457. }
  458. }
  459. waitpid(-1, NULL, WNOHANG);
  460. }
  461. return EXIT_SUCCESS;
  462. }