Download | Plain Text | No Line Numbers


  1. /*
  2.  * ProFTPD: mod_dovecot_anvil -- slows down bruteforce attacks with the use
  3.  * of dovecot's anvil/penalty backend
  4.  *
  5.  * Copyright (c) 2016 Manuel Mausz
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  20.  */
  21.  
  22. #define MOD_DOVECOT_ANVIL_VERSION "mod_dovecot_anvil/0.3.0"
  23.  
  24. #define ANVIL_DEFAULT_URI "/var/run/dovecot/anvil-auth-penalty"
  25. #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n"
  26. #define ANVIL_PENALTY_INIT_SECS 2
  27. #define ANVIL_PENALTY_MAX_SECS 60
  28. #define ANVIL_PENALTY_IPV6_MASK_BITS 48
  29.  
  30. #include "conf.h"
  31. #include "privs.h"
  32.  
  33. static const char *trace_channel = "dovecot_anvil";
  34.  
  35. static struct {
  36. int fd;
  37. const char *ident;
  38. int penalty;
  39. uint32_t checksum;
  40. } anvil = { .fd = -1, .checksum = 0 };
  41.  
  42. module dovecot_anvil_module;
  43.  
  44. static int dovecot_anvil_sess_init(void);
  45. static uint32_t crc32_str(const char *str);
  46. static uint32_t crc32_str_more(uint32_t crc, const char *str);
  47.  
  48. static const char *anvil_get_ident(pool *p)
  49. {
  50. const char *addr = pr_table_get(session.notes, "mod_xclient.addr", NULL);
  51. const pr_netaddr_t *remote_addr = (addr)
  52. ? pr_netaddr_get_addr2(session.pool, addr, NULL, PR_NETADDR_GET_ADDR_FL_EXCL_DNS)
  53. : pr_netaddr_get_sess_remote_addr();
  54. pr_trace_msg(trace_channel, 5, "fetching ident for %s",
  55. pr_netaddr_get_ipstr(remote_addr));
  56.  
  57. #ifdef PR_USE_IPV6
  58. if (pr_netaddr_use_ipv6() && pr_netaddr_get_family(remote_addr) == AF_INET6) {
  59. pr_netaddr_t *remote_addr4 = pr_netaddr_v6tov4(p, remote_addr);
  60. if (remote_addr4 != NULL)
  61. return pr_netaddr_get_ipstr(remote_addr4);
  62.  
  63. struct in6_addr ip = *(struct in6_addr *)pr_netaddr_get_inaddr(remote_addr);
  64. memset(ip.s6_addr + ANVIL_PENALTY_IPV6_MASK_BITS/CHAR_BIT, 0,
  65. sizeof(ip.s6_addr) - ANVIL_PENALTY_IPV6_MASK_BITS/CHAR_BIT);
  66. char remote_ip6[INET6_ADDRSTRLEN];
  67. pr_inet_ntop(AF_INET6, &ip, remote_ip6, INET6_ADDRSTRLEN);
  68. return pstrdup(p, remote_ip6);
  69. }
  70. #endif
  71. return pr_netaddr_get_ipstr(remote_addr);
  72. }
  73.  
  74. static int net_connect_inet(const char *host, const char *port)
  75. {
  76. struct addrinfo hints = {
  77. .ai_family = AF_UNSPEC,
  78. .ai_socktype = SOCK_STREAM
  79. };
  80. struct addrinfo *result;
  81.  
  82. pr_trace_msg(trace_channel, 5, "connecting to tcp://%s:%s", host, port);
  83. int s = getaddrinfo(host, port, &hints, &result);
  84. if (s != 0) {
  85. pr_log_pri(PR_LOG_ERR, MOD_DOVECOT_ANVIL_VERSION ": getaddrinfo: %s",
  86. gai_strerror(s));
  87. return -1;
  88. }
  89.  
  90. //FIXME: connect might hang
  91. int sockfd = -1;
  92. struct addrinfo *r;
  93. for (r = result; r != NULL; r=r->ai_next) {
  94. sockfd = socket(r->ai_family, r->ai_socktype | SOCK_CLOEXEC,
  95. r->ai_protocol);
  96. if (sockfd == -1)
  97. continue;
  98. if (connect(sockfd, r->ai_addr, r->ai_addrlen) != -1)
  99. break; /* success */
  100. close(sockfd);
  101. sockfd = -1;
  102. break; /* we only try the first record */
  103. }
  104.  
  105. freeaddrinfo(result);
  106. return sockfd;
  107. }
  108.  
  109. static int net_connect_unix(const char *path)
  110. {
  111. pr_trace_msg(trace_channel, 5, "connecting to unix://%s", path);
  112. int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
  113. if (sockfd < 0)
  114. return -1;
  115.  
  116. struct sockaddr_un saddr;
  117. saddr.sun_family = AF_UNIX;
  118. strcpy(saddr.sun_path, path);
  119. PRIVS_ROOT
  120. int conn = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
  121. PRIVS_RELINQUISH
  122. if (conn != -1)
  123. return sockfd;
  124. close(sockfd);
  125. return -1;
  126. }
  127.  
  128. static int anvil_connect(config_rec *c)
  129. {
  130. int sockfd = (c->argc == 2) ? net_connect_inet(c->argv[0], c->argv[1]) :
  131. net_connect_unix(c->argv[0]);
  132. if (sockfd < 0) {
  133. pr_log_pri(PR_LOG_ERR, MOD_DOVECOT_ANVIL_VERSION
  134. ": Unable to connect to socket: %s", strerror(errno));
  135. return -1;
  136. }
  137. if (send(sockfd, ANVIL_HANDSHAKE, strlen(ANVIL_HANDSHAKE), 0)
  138. != strlen(ANVIL_HANDSHAKE)) {
  139. pr_log_pri(PR_LOG_ERR, MOD_DOVECOT_ANVIL_VERSION
  140. ": Error while sending handshake: %s", strerror(errno));
  141. close(sockfd);
  142. return -1;
  143. }
  144. return sockfd;
  145. }
  146.  
  147. typedef int anvil_penalty_callback_t(uint16_t penalty,
  148. unsigned long last_penalty);
  149. static int anvil_penalty_get(int sockfd, const char *ident,
  150. anvil_penalty_callback_t *callback)
  151. {
  152. char buf[100];
  153. int len = sprintf(buf, "PENALTY-GET\t%s\n", ident);
  154. if (send(sockfd, buf, len, 0) != len)
  155. return -1;
  156.  
  157. if ((len = recv(sockfd, &buf, sizeof(buf), 0)) < 0)
  158. return -1;
  159. buf[len] = '\0';
  160.  
  161. uint16_t penalty = 0;
  162. unsigned long last_penalty = 0;
  163. if (sscanf(buf, "%" SCNu16 " %lu", &penalty, &last_penalty) != 2) {
  164. pr_log_pri(PR_LOG_ERR, MOD_DOVECOT_ANVIL_VERSION
  165. "Invalid PENALTY-GET reply: %s", buf);
  166. return -1;
  167. }
  168.  
  169. pr_trace_msg(trace_channel, 5, "penalty for '%s' is %u", ident, penalty);
  170. return callback(penalty, last_penalty);
  171. }
  172.  
  173. static void anvil_penalty_inc(int sockfd, const char *ident, uint32_t checksum)
  174. {
  175. char buf[100];
  176. pr_trace_msg(trace_channel, 5, "incrementing penalty for '%s'", ident);
  177. int len = sprintf(buf, "PENALTY-INC\t%s\t%u\n", ident, checksum);
  178. (void)send(sockfd, buf, len, 0);
  179. }
  180.  
  181. static void anvil_penalty_set(int sockfd, const char *ident, uint16_t value)
  182. {
  183. char buf[100];
  184. pr_trace_msg(trace_channel, 5, "setting penalty for '%s' to %" PRIu16,
  185. ident, value);
  186. int len = sprintf(buf, "PENALTY-SET\t%s\t%u\n", ident, value);
  187. (void)send(sockfd, buf, len, 0);
  188. }
  189.  
  190. static unsigned int anvil_penalty_to_secs(uint16_t penalty)
  191. {
  192. unsigned int i, secs = ANVIL_PENALTY_INIT_SECS;
  193.  
  194. for (i = 0; i < penalty; i++)
  195. secs *= 2;
  196. return secs < ANVIL_PENALTY_MAX_SECS ? secs : ANVIL_PENALTY_MAX_SECS;
  197. }
  198.  
  199. static void mask_sigalarm(unsigned char block)
  200. {
  201. static sigset_t mask_sigset;
  202.  
  203. if (block) {
  204. sigemptyset(&mask_sigset);
  205. sigaddset(&mask_sigset, SIGALRM);
  206.  
  207. if (sigprocmask(SIG_BLOCK, &mask_sigset, NULL) < 0)
  208. pr_log_pri(PR_LOG_NOTICE, "unable to block signal set: %s",
  209. strerror(errno));
  210. }
  211. else {
  212. if (sigprocmask(SIG_UNBLOCK, &mask_sigset, NULL) < 0)
  213. pr_log_pri(PR_LOG_NOTICE, "unable to unblock signal set: %s",
  214. strerror(errno));
  215. }
  216. }
  217.  
  218. static int anvil_penalty_callback_sleep(uint16_t penalty,
  219. unsigned long last_penalty)
  220. {
  221. if (penalty > 0) {
  222. unsigned int secs = anvil_penalty_to_secs(penalty);
  223. pr_trace_msg(trace_channel, 5, "sleeping %u secs", secs);
  224.  
  225. mask_sigalarm(TRUE);
  226. struct timeval tv = { .tv_sec = secs, .tv_usec = 0 };
  227. (void)select(0, NULL, NULL, NULL, &tv);
  228. mask_sigalarm(FALSE);
  229.  
  230. pr_timer_reset(PR_TIMER_LOGIN, ANY_MODULE);
  231. }
  232. return penalty;
  233. }
  234.  
  235. /* command handlers */
  236. MODRET dovecot_anvil_pre_pass(cmd_rec *cmd)
  237. {
  238. const char *user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
  239. if (user == NULL)
  240. return PR_DECLINED(cmd);
  241.  
  242. config_rec *c;
  243. c = find_config(main_server->conf, CONF_PARAM, "DovecotAnvil", FALSE);
  244. if (!c)
  245. {
  246. pr_trace_msg(trace_channel, 5, "directive DovecotAnvil not enabled."
  247. " mod_dovecot_anvil not enabled.");
  248. return PR_DECLINED(cmd);
  249. }
  250.  
  251. // our auth handler will be called in the middle of command handling, so
  252. // lifetime of cmd->pool is ok
  253. if ((anvil.ident = anvil_get_ident(cmd->pool)) == NULL)
  254. {
  255. pr_log_pri(PR_LOG_ERR, MOD_DOVECOT_ANVIL_VERSION ": Unable to get ident");
  256. return PR_DECLINED(cmd);
  257. }
  258.  
  259. if ((anvil.fd = anvil_connect(c)) == -1)
  260. return PR_DECLINED(cmd);
  261.  
  262. anvil.penalty = anvil_penalty_get(anvil.fd, anvil.ident,
  263. anvil_penalty_callback_sleep);
  264. if (anvil.penalty == -1) {
  265. pr_log_pri(PR_LOG_ERR, MOD_DOVECOT_ANVIL_VERSION
  266. "Error while fetching penalty: %s", strerror(errno));
  267. close(anvil.fd);
  268. anvil.fd = -1;
  269. return PR_DECLINED(cmd);
  270. }
  271.  
  272. return PR_DECLINED(cmd);
  273. }
  274.  
  275. MODRET dovecot_anvil_post_pass(cmd_rec *cmd)
  276. {
  277. if (anvil.fd != -1) {
  278. (void)anvil_penalty_set(anvil.fd, anvil.ident, 0);
  279. close(anvil.fd);
  280. anvil.fd = -1;
  281. }
  282. return PR_DECLINED(cmd);
  283. }
  284.  
  285. MODRET dovecot_anvil_post_pass_err(cmd_rec *cmd)
  286. {
  287. if (anvil.fd != -1) {
  288. int do_increment = TRUE;
  289. if ((cmd->cmd_class & CL_SFTP) || (cmd->cmd_class & CL_SSH)) {
  290. const char *sftp_method = pr_env_get(cmd->pool, "SFTP_USER_AUTH_METHOD");
  291. do_increment = (sftp_method != NULL && strncmp(sftp_method, "password", 9) == 0)
  292. ? TRUE : FALSE;
  293. }
  294. if (do_increment)
  295. (void)anvil_penalty_inc(anvil.fd, anvil.ident, anvil.checksum);
  296. close(anvil.fd);
  297. anvil.fd = -1;
  298. }
  299. return PR_DECLINED(cmd);
  300. }
  301.  
  302. /* configuration handlers */
  303. MODRET set_dovecot_anvil(cmd_rec *cmd)
  304. {
  305. CHECK_ARGS(cmd, 1);
  306. CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  307.  
  308. int val = get_boolean(cmd, 1);
  309. if (val == TRUE)
  310. add_config_param_str(cmd->argv[0], 1, ANVIL_DEFAULT_URI);
  311. else if (val == -1) {
  312. if (strncmp(cmd->argv[1], "tcp://", strlen("tcp://")) == 0) {
  313. config_rec *c = add_config_param(cmd->argv[0], 2, NULL, NULL);
  314. char *path = pstrdup(c->pool, cmd->argv[1] + strlen("tcp://"));
  315.  
  316. char *portptr = rindex(path, ':');
  317. if (portptr == NULL)
  318. CONF_ERROR(cmd, "Invalid uri: port missing?");
  319. *portptr = '\0';
  320.  
  321. char *host = path;
  322. if (host[0] == '[' && *(portptr - 1) == ']') {
  323. host++;
  324. *(portptr - 1) = '\0';
  325. }
  326.  
  327. c->argv[0] = host;
  328. c->argv[1] = portptr + 1;
  329. }
  330. else if (strncmp(cmd->argv[1], "unix://", strlen("unix://")) == 0)
  331. add_config_param_str(cmd->argv[0], 1, cmd->argv[1] + strlen("unix://"));
  332. else
  333. CONF_ERROR(cmd, "has invalid uri syntax");
  334. }
  335.  
  336. return PR_HANDLED(cmd);
  337. }
  338.  
  339. /* authentication handlers */
  340. MODRET dovecot_anvil_auth(cmd_rec *cmd)
  341. {
  342. if (cmd->argc != 2)
  343. return PR_DECLINED(cmd);
  344. if (anvil.fd != -1)
  345. anvil.checksum = crc32_str_more(crc32_str(cmd->argv[1]), cmd->argv[0]);
  346. return PR_DECLINED(cmd);
  347. }
  348.  
  349. /* event handlers */
  350. #if defined(PR_SHARED_MODULE)
  351. static void dovecot_anvil_mod_unload_ev(const void *event_data, void *user_data)
  352. {
  353. if (strcmp("mod_dovecot_anvil.c", (const char *)event_data) == 0)
  354. pr_event_unregister(&dovecot_anvil_module, NULL, NULL);
  355. }
  356. #endif
  357.  
  358. static void dovecot_anvil_sess_reinit_ev(const void *event_data, void *user_data)
  359. {
  360. /* A HOST command changed the main_server pointer, reinitialize ourselves. */
  361. pr_event_unregister(&dovecot_anvil_module, "core.session-reinit",
  362. dovecot_anvil_sess_reinit_ev);
  363. pr_auth_remove_auth_only_module("mod_dovecot_anvil.c");
  364.  
  365. if (dovecot_anvil_sess_init() < 0)
  366. pr_session_disconnect(&dovecot_anvil_module,
  367. PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
  368. }
  369.  
  370. /* initialization routines */
  371. static int dovecot_anvil_sess_init(void)
  372. {
  373. pr_event_register(&dovecot_anvil_module, "core.session-reinit",
  374. dovecot_anvil_sess_reinit_ev, NULL);
  375.  
  376. config_rec *c;
  377. c = find_config(main_server->conf, CONF_PARAM, "DovecotAnvil", FALSE);
  378. if (!c)
  379. {
  380. pr_trace_msg(trace_channel, 5, "directive DovecotAnvil not enabled."
  381. " mod_dovecot_anvil not enabled.");
  382. return 0;
  383. }
  384.  
  385. if (pr_auth_add_auth_only_module("mod_dovecot_anvil.c") < 0) {
  386. int xerrno = errno;
  387.  
  388. pr_log_pri(PR_LOG_NOTICE, MOD_DOVECOT_ANVIL_VERSION
  389. ": unable to add 'mod_dovecot_anvil.c' as an auth-only module: %s",
  390. strerror(xerrno));
  391.  
  392. errno = xerrno;
  393. return -1;
  394. }
  395. return 0;
  396. }
  397.  
  398. static int dovecot_anvil_init(void)
  399. {
  400. #if defined(PR_SHARED_MODULE)
  401. pr_event_register(&dovecot_anvil_module, "core.module-unload",
  402. dovecot_anvil_mod_unload_ev, NULL);
  403. #endif
  404. return 0;
  405. }
  406.  
  407. /* module api tables */
  408. static conftable dovecot_anvil_conftab[] =
  409. {
  410. { "DovecotAnvil", set_dovecot_anvil, NULL },
  411. { NULL }
  412. };
  413.  
  414. static cmdtable dovecot_anvil_cmdtab[] =
  415. {
  416. { PRE_CMD, C_PASS, G_NONE, dovecot_anvil_pre_pass,
  417. FALSE, FALSE, CL_AUTH },
  418. { POST_CMD, C_PASS, G_NONE, dovecot_anvil_post_pass,
  419. FALSE, FALSE, CL_AUTH },
  420. { POST_CMD_ERR, C_PASS, G_NONE, dovecot_anvil_post_pass_err,
  421. FALSE, FALSE, CL_AUTH },
  422. { 0, NULL }
  423. };
  424.  
  425. static authtable dovecot_anvil_authtab[] = {
  426. { 0, "auth", dovecot_anvil_auth },
  427. { 0, NULL, NULL }
  428. };
  429.  
  430. module dovecot_anvil_module =
  431. {
  432. /* always NULL */
  433. NULL, NULL,
  434.  
  435. /* module api version 2.0 */
  436. 0x20,
  437.  
  438. /* module name */
  439. "dovecot_anvil",
  440.  
  441. /* module configuration handler table */
  442. dovecot_anvil_conftab,
  443.  
  444. /* module command handler table */
  445. dovecot_anvil_cmdtab,
  446.  
  447. /* module authentication handler table */
  448. dovecot_anvil_authtab,
  449.  
  450. /* module initialization function */
  451. dovecot_anvil_init,
  452.  
  453. /* module session initialization function */
  454. dovecot_anvil_sess_init,
  455.  
  456. /* module version */
  457. MOD_DOVECOT_ANVIL_VERSION
  458. };
  459.  
  460. /* copied from dovecot/lib/crc32.c, MIT licence */
  461. static uint32_t crc32tab[256] = {
  462. 0x00000000,
  463. 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
  464. 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E,
  465. 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
  466. 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
  467. 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0,
  468. 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63,
  469. 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
  470. 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA,
  471. 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75,
  472. 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180,
  473. 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
  474. 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87,
  475. 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
  476. 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
  477. 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
  478. 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4,
  479. 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B,
  480. 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,
  481. 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
  482. 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541,
  483. 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
  484. 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F,
  485. 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
  486. 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E,
  487. 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
  488. 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C,
  489. 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
  490. 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B,
  491. 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2,
  492. 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671,
  493. 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
  494. 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8,
  495. 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767,
  496. 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6,
  497. 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
  498. 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795,
  499. 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
  500. 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
  501. 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
  502. 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82,
  503. 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
  504. 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8,
  505. 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
  506. 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF,
  507. 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE,
  508. 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D,
  509. 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
  510. 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
  511. 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
  512. 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02,
  513. 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
  514. };
  515.  
  516. static uint32_t crc32_str(const char *str)
  517. {
  518. return crc32_str_more(0, str);
  519. }
  520.  
  521. static uint32_t crc32_str_more(uint32_t crc, const char *str)
  522. {
  523. const uint8_t *p = (const uint8_t *)str;
  524.  
  525. crc ^= 0xffffffff;
  526. for (; *p != '\0'; p++)
  527. crc = (crc >> 8) ^ crc32tab[((crc ^ *p) & 0xff)];
  528. crc ^= 0xffffffff;
  529. return crc;
  530. }
  531.