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.

postsrsd.c 18KB

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