4
0

acl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. /*-
  2. * acl.c - a small library for nrpe.c. It adds IPv4 subnets support to ACL in nrpe.
  3. * Copyright (c) 2011 Kaspersky Lab ZAO
  4. * Last Modified: 08-10-2011 by Konstantin Malov with Oleg Koreshkov's help
  5. *
  6. * Description:
  7. * acl.c creates two linked lists. One is for IPv4 hosts and networks, another is for domain names.
  8. * All connecting hosts (if allowed_hosts is defined) are checked in these two lists.
  9. *
  10. * Some notes:
  11. * 1) IPv6 isn't supported in ACL.
  12. * 2) Only ANCII names are supported in ACL.
  13. *
  14. * License: GPL
  15. *
  16. * This program is free software; you can redistribute it and/or modify
  17. * it under the terms of the GNU General Public License as published by
  18. * the Free Software Foundation; either version 2 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU General Public License
  27. * along with this program; if not, write to the Free Software
  28. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  29. */
  30. #include "../include/config.h"
  31. #include <sys/types.h>
  32. #include <sys/socket.h>
  33. #include <netinet/in.h>
  34. #include <arpa/inet.h>
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <ctype.h>
  39. #include <netdb.h>
  40. #include <syslog.h>
  41. #include <stdarg.h>
  42. #include "../include/acl.h"
  43. /* This function checks if a char argumnet from valid char range.
  44. * Valid range is: ASCII only, a number or a letter, a space, a dot, a slash, a dash, a comma.
  45. *
  46. * Returns:
  47. * 0 - char isn't from valid group
  48. * 1 - char is a number
  49. * 2 - char is a letter
  50. * 3 - char is a space(' ')
  51. * 4 - char is a dot('.')
  52. * 5 - char is a slash('/')
  53. * 6 - char is a dash('-')
  54. * 7 - char is a comma(',')
  55. */
  56. int isvalidchar(int c) {
  57. if (!isascii(c))
  58. return 0;
  59. if (isdigit(c))
  60. return 1;
  61. if (isalpha(c))
  62. return 2;
  63. if (isspace(c))
  64. return 3;
  65. switch (c) {
  66. case '.':
  67. return 4;
  68. break;
  69. case '/':
  70. return 5;
  71. break;
  72. case '-':
  73. return 6;
  74. break;
  75. case ',':
  76. return 7;
  77. break;
  78. default:
  79. return 0;
  80. }
  81. }
  82. /*
  83. * Get substring from allowed_hosts from s position to e position.
  84. */
  85. char * acl_substring(char *string, int s, int e) {
  86. char *substring;
  87. int len = e - s;
  88. if (len < 0)
  89. return NULL;
  90. if ( (substring = malloc(len + 1)) == NULL)
  91. return NULL;
  92. memmove(substring, string + s, len + 1);
  93. return substring;
  94. }
  95. /*
  96. * Add IPv4 host or network to IP ACL. IPv4 format is X.X.X.X[/X].
  97. * Host will be added to ACL only if it has passed IPv4 format check.
  98. *
  99. * Returns:
  100. * 1 - on success
  101. * 0 - on failure
  102. *
  103. * States for IPv4 format check:
  104. * 0 - numbers(-> 1), dot(-> -1), slash(-> -1), other(-> -1)
  105. * 1 - numbers(-> 1), dot(-> 2), slash(-> -1), other(-> -1)
  106. * 2 - numbers(-> 3), dot(-> -1), slash(-> -1), other(-> -1)
  107. * 3 - numbers(-> 3), dot(-> 4), slash(-> -1), other(-> -1)
  108. * 4 - numbers(-> 5), dot(-> -1), slash(-> -1), other(-> -1)
  109. * 5 - numbers(-> 5), dot(-> 6), slash(-> -1), other(-> -1)
  110. * 6 - numbers(-> 7), dot(-> -1), slash(-> -1), other(-> -1)
  111. * 7 - numbers(-> 7), dor(-> -1), slash(-> 8), other(-> -1)
  112. * 8 - numbers(-> 9), dor(-> -1), slash(-> -1), other(-> -1)
  113. * 9 - numbers(-> 9), dot(-> -1), slash(-> -1), other(-> -1)
  114. *
  115. * Good states are 7(IPv4 host) and 9(IPv4 network)
  116. */
  117. int add_ipv4_to_acl(char *ipv4) {
  118. int state = 0;
  119. int octet = 0;
  120. int index = 0; /* position in data array */
  121. int data[5]; /* array to store ip octets and mask */
  122. int len = strlen(ipv4);
  123. int i, c;
  124. unsigned long ip, mask;
  125. struct ip_acl *ip_acl_curr;
  126. /* Check for min and max IPv4 valid length */
  127. if (len < 7 || len > 18)
  128. return 0;
  129. /* default mask for ipv4 */
  130. data[4] = 32;
  131. /* Basic IPv4 format check */
  132. for (i = 0; i < len; i++) {
  133. /* Return 0 on error state */
  134. if (state == -1)
  135. return 0;
  136. c = ipv4[i];
  137. switch (c) {
  138. case '0': case '1': case '2': case '3': case '4':
  139. case '5': case '6': case '7': case '8': case '9':
  140. octet = octet * 10 + CHAR_TO_NUMBER(c);
  141. switch (state) {
  142. case 0: case 2: case 4: case 6: case 8:
  143. state++;
  144. break;
  145. }
  146. break;
  147. case '.':
  148. switch (state) {
  149. case 1: case 3: case 5:
  150. data[index++] = octet;
  151. octet = 0;
  152. state++;
  153. break;
  154. default:
  155. state = -1;
  156. }
  157. break;
  158. case '/':
  159. switch (state) {
  160. case 7:
  161. data[index++] = octet;
  162. octet = 0;
  163. state++;
  164. break;
  165. default:
  166. state = -1;
  167. }
  168. break;
  169. default:
  170. state = -1;
  171. }
  172. }
  173. /* Exit state handling */
  174. switch (state) {
  175. case 7: case 9:
  176. data[index] = octet;
  177. break;
  178. default:
  179. /* Bad states */
  180. return 0;
  181. }
  182. /*
  183. * Final IPv4 format check.
  184. */
  185. for (i=0; i < 4; i++) {
  186. if (data[i] < 0 || data[i] > 255) {
  187. syslog(LOG_ERR,"Invalid IPv4 address/network format(%s) in allowed_hosts option\n",ipv4);
  188. return 0;
  189. }
  190. }
  191. if (data[4] < 0 || data[4] > 32) {
  192. syslog(LOG_ERR,"Invalid IPv4 network mask format(%s) in allowed_hosts option\n",ipv4);
  193. return 0;
  194. }
  195. /* Conver ip and mask to unsigned long */
  196. ip = htonl((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]);
  197. mask = htonl(-1 << (32 - data[4]));
  198. /* Wrong network address */
  199. if ( (ip & mask) != ip) {
  200. syslog(LOG_ERR,"IP address and mask do not match in %s\n",ipv4);
  201. return 0;
  202. }
  203. /* Add addr to ip_acl list */
  204. if ( (ip_acl_curr = malloc(sizeof(*ip_acl_curr))) == NULL) {
  205. syslog(LOG_ERR,"Can't allocate memory for ACL, malloc error\n");
  206. return 0;
  207. }
  208. /* Save result in ACL ip list */
  209. ip_acl_curr->family = AF_INET;
  210. ip_acl_curr->addr.s_addr = ip;
  211. ip_acl_curr->mask.s_addr = mask;
  212. ip_acl_curr->next = NULL;
  213. if (ip_acl_head == NULL) {
  214. ip_acl_head = ip_acl_curr;
  215. } else {
  216. ip_acl_prev->next = ip_acl_curr;
  217. }
  218. ip_acl_prev = ip_acl_curr;
  219. return 1;
  220. }
  221. /*
  222. * Add IPv6 host or network to IP ACL. Host will be added to ACL only if
  223. * it has passed IPv6 format check.
  224. *
  225. */
  226. int add_ipv6_to_acl(char *ipv6) {
  227. char *ipv6tmp;
  228. char *addr_part, *mask_part;
  229. struct in6_addr addr;
  230. struct in6_addr mask;
  231. int maskval;
  232. int byte, bit;
  233. int nbytes = sizeof(mask.s6_addr) / sizeof(mask.s6_addr[0]);
  234. int x;
  235. struct ip_acl *ip_acl_curr;
  236. /* Save temporary copy of ipv6 so we can use the original in error
  237. messages if needed */
  238. ipv6tmp = strdup(ipv6);
  239. if(NULL == ipv6tmp) {
  240. syslog(LOG_ERR, "Memory allocation failed for copy of address: %s\n",
  241. ipv6);
  242. return 0;
  243. }
  244. addr_part = ipv6tmp;
  245. mask_part = strchr(ipv6tmp, '/');
  246. if (mask_part) {
  247. *mask_part = '\0';
  248. ++mask_part;
  249. }
  250. /* Parse the address itself */
  251. if(inet_pton(AF_INET6, addr_part, &addr) <= 0) {
  252. free(ipv6tmp);
  253. return 0;
  254. }
  255. /* Check whether there is a netmask */
  256. if (mask_part && *mask_part) {
  257. /* If so, build a netmask */
  258. /* Get the number of bits in the mask */
  259. maskval = atoi(mask_part);
  260. if(maskval < 0 || maskval > 128) {
  261. free(ipv6tmp);
  262. return 0;
  263. }
  264. /* Initialize to zero */
  265. for(x = 0; x < nbytes; x++) {
  266. mask.s6_addr[x] = 0;
  267. }
  268. /* Set mask based on mask bits */
  269. byte = 0;
  270. bit = 7;
  271. while(maskval > 0) {
  272. mask.s6_addr[byte] |= 1 << bit;
  273. bit -= 1;
  274. if(bit < 0) {
  275. bit = 7;
  276. byte++;
  277. }
  278. maskval--;
  279. }
  280. }
  281. else {
  282. /* Otherwise, this is a single address */
  283. for(x = 0; x < nbytes; x++) {
  284. mask.s6_addr[x] = 0xFF;
  285. }
  286. }
  287. /* Add address to ip_acl list */
  288. ip_acl_curr = malloc(sizeof(*ip_acl_curr));
  289. if(NULL == ip_acl_curr) {
  290. syslog(LOG_ERR, "Memory allocation failed for ACL: %s\n", ipv6);
  291. return 0;
  292. }
  293. /* Save result in ACL ip list */
  294. ip_acl_curr->family = AF_INET6;
  295. for(x = 0; x < nbytes; x++) {
  296. ip_acl_curr->addr6.s6_addr[x] =
  297. addr.s6_addr[x] & mask.s6_addr[x];
  298. ip_acl_curr->mask6.s6_addr[x] = mask.s6_addr[x];
  299. }
  300. ip_acl_curr->next = NULL;
  301. if(NULL == ip_acl_head) {
  302. ip_acl_head = ip_acl_curr;
  303. }
  304. else {
  305. ip_acl_prev->next = ip_acl_curr;
  306. }
  307. ip_acl_prev = ip_acl_curr;
  308. free(ipv6tmp);
  309. return 1;
  310. }
  311. /*
  312. * Add domain to DNS ACL list
  313. * Domain will be added only if it has passed domain name check.
  314. *
  315. * In this case domain valid format is:
  316. * 1) Domain names must use only alphanumeric characters and dashes (-).
  317. * 2) Domain names mustn't begin or end with dashes (-).
  318. * 3) Domain names mustn't have more than 63 characters.
  319. *
  320. * Return:
  321. * 1 - for success
  322. * 0 - for failure
  323. *
  324. * 0 - alpha(-> 1), number(-> 1), dot(-> -1), dash(-> -1), all other(-> -1)
  325. * 1 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  326. * 2 - alpha(-> 3), number(-> 1), dot(-> -1), dash(-> -1), all other(-> -1)
  327. * 3 - alpha(-> 4), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  328. * 4 - alpha(-> 5), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  329. * 5 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  330. * 6 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  331. * For real FQDN only 4 and 5 states are good for exit.
  332. * I don't check if top domain exists (com, ru and etc.)
  333. * But in real life NRPE could work in LAN,
  334. * with local domain zones like .local or with names like 'mars' added to /etc/hosts.
  335. * So 1 is good state too. And maybe this check is not necessary at all...
  336. */
  337. int add_domain_to_acl(char *domain) {
  338. int state = 0;
  339. int len = strlen(domain);
  340. int i, c;
  341. struct dns_acl *dns_acl_curr;
  342. if (len > 63)
  343. return 0;
  344. for (i = 0; i < len; i++) {
  345. c = domain[i];
  346. switch (isvalidchar(c)) {
  347. case 1:
  348. state = 1;
  349. break;
  350. case 2:
  351. switch (state) {
  352. case 0: case 1: case 5: case 6:
  353. state = 1;
  354. break;
  355. case 2: case 3: case 4:
  356. state++;
  357. break;
  358. }
  359. break;
  360. case 4:
  361. switch (state) {
  362. case 0: case 2:
  363. state = -1;
  364. break;
  365. default:
  366. state = 2;
  367. }
  368. break;
  369. case 6:
  370. switch (state) {
  371. case 0: case 2:
  372. state = -1;
  373. break;
  374. default:
  375. state = 6;
  376. }
  377. break;
  378. default:
  379. /* Not valid chars */
  380. return 0;
  381. }
  382. }
  383. /* Check exit code */
  384. switch (state) {
  385. case 1: case 4: case 5:
  386. /* Add name to domain ACL list */
  387. if ( (dns_acl_curr = malloc(sizeof(*dns_acl_curr))) == NULL) {
  388. syslog(LOG_ERR,"Can't allocate memory for ACL, malloc error\n");
  389. return 0;
  390. }
  391. strcpy(dns_acl_curr->domain, domain);
  392. dns_acl_curr->next = NULL;
  393. if (dns_acl_head == NULL)
  394. dns_acl_head = dns_acl_curr;
  395. else
  396. dns_acl_prev->next = dns_acl_curr;
  397. dns_acl_prev = dns_acl_curr;
  398. return 1;
  399. default:
  400. return 0;
  401. }
  402. }
  403. /* Checks connectiong host in ACL
  404. *
  405. * Returns:
  406. * 1 - on success
  407. * 0 - on failure
  408. */
  409. int is_an_allowed_host(int family, void *host)
  410. {
  411. struct ip_acl *ip_acl_curr = ip_acl_head;
  412. int nbytes;
  413. int x;
  414. struct dns_acl *dns_acl_curr = dns_acl_head;
  415. struct sockaddr_in *addr;
  416. struct sockaddr_in6 addr6;
  417. struct addrinfo *res, *ai;
  418. while (ip_acl_curr != NULL) {
  419. if(ip_acl_curr->family == family) {
  420. switch(ip_acl_curr->family) {
  421. case AF_INET:
  422. if((((struct in_addr *)host)->s_addr &
  423. ip_acl_curr->mask.s_addr) ==
  424. ip_acl_curr->addr.s_addr) {
  425. return 1;
  426. }
  427. break;
  428. case AF_INET6:
  429. nbytes = sizeof(ip_acl_curr->mask6.s6_addr) /
  430. sizeof(ip_acl_curr->mask6.s6_addr[0]);
  431. for(x = 0; x < nbytes; x++) {
  432. if((((struct in6_addr *)host)->s6_addr[x] &
  433. ip_acl_curr->mask6.s6_addr[x]) !=
  434. ip_acl_curr->addr6.s6_addr[x]) {
  435. break;
  436. }
  437. }
  438. if(x == nbytes) {
  439. /* All bytes in host's address pass the netmask mask */
  440. return 1;
  441. }
  442. break;
  443. }
  444. }
  445. ip_acl_curr = ip_acl_curr->next;
  446. }
  447. while(dns_acl_curr != NULL) {
  448. if (!getaddrinfo(dns_acl_curr->domain, NULL, NULL, &res)) {
  449. for (ai = res; ai; ai = ai->ai_next) {
  450. switch(ai->ai_family) {
  451. case AF_INET:
  452. addr = (struct sockaddr_in*)(ai->ai_addr);
  453. if (addr->sin_addr.s_addr == ((struct in_addr*)host)->s_addr)
  454. return 1;
  455. break;
  456. case AF_INET6:
  457. memcpy((char*)&addr6, ai->ai_addr, sizeof(addr6));
  458. if (!memcmp(&addr6.sin6_addr, &host, sizeof(addr6.sin6_addr)))
  459. return 1;
  460. break;
  461. }
  462. }
  463. dns_acl_curr = dns_acl_curr->next;
  464. }
  465. }
  466. return 0;
  467. }
  468. /* The trim() function takes a source string and copies it to the destination string,
  469. * stripped of leading and training whitespace. The destination string must be
  470. * allocated at least as large as the source string.
  471. */
  472. void trim( char *src, char *dest) {
  473. char *sptr, *dptr;
  474. for( sptr = src; isspace( *sptr) && *sptr; sptr++); /* Jump past leading spaces */
  475. for( dptr = dest; !isspace( *sptr) && *sptr; ) {
  476. *dptr = *sptr;
  477. sptr++;
  478. dptr++;
  479. }
  480. *dptr = '\0';
  481. return;
  482. }
  483. /* This function splits allowed_hosts to substrings with comma(,) as a delimiter.
  484. * It doesn't check validness of ACL record (add_ipv4_to_acl() and add_domain_to_acl() do),
  485. * just trims spaces from ACL records.
  486. * After this it sends ACL records to add_ipv4_to_acl() or add_domain_to_acl().
  487. */
  488. void parse_allowed_hosts(char *allowed_hosts) {
  489. char *hosts = strdup( allowed_hosts); /* Copy since strtok* modifies original */
  490. char *saveptr;
  491. char *tok;
  492. const char *delim = ",";
  493. char *trimmed_tok;
  494. #ifdef HAVE_STRTOK_R
  495. tok = strtok_r(hosts, delim, &saveptr);
  496. #else
  497. tok = strtok(hosts, delim);
  498. #endif
  499. while( tok) {
  500. trimmed_tok = malloc( sizeof( char) * ( strlen( tok) + 1));
  501. trim( tok, trimmed_tok);
  502. if( strlen( trimmed_tok) > 0) {
  503. if (!add_ipv4_to_acl(trimmed_tok) && !add_ipv6_to_acl(trimmed_tok)
  504. && !add_domain_to_acl(trimmed_tok)) {
  505. syslog(LOG_ERR,"Can't add to ACL this record (%s). Check allowed_hosts option!\n",trimmed_tok);
  506. }
  507. }
  508. free( trimmed_tok);
  509. #ifdef HAVE_STRTOK_R
  510. tok = strtok_r(NULL, delim, &saveptr);
  511. #else
  512. tok = strtok(NULL, delim);
  513. #endif
  514. }
  515. free( hosts);
  516. }
  517. /*
  518. * Converts mask in unsigned long format to two digit prefix
  519. */
  520. unsigned int prefix_from_mask(struct in_addr mask) {
  521. int prefix = 0;
  522. unsigned long bit = 1;
  523. int i;
  524. for (i = 0; i < 32; i++) {
  525. if (mask.s_addr & bit)
  526. prefix++;
  527. bit = bit << 1;
  528. }
  529. return (prefix);
  530. }
  531. /*
  532. * It shows all hosts in ACL lists
  533. */
  534. void show_acl_lists(void) {
  535. struct ip_acl *ip_acl_curr = ip_acl_head;
  536. struct dns_acl *dns_acl_curr = dns_acl_head;
  537. while (ip_acl_curr != NULL) {
  538. printf(" IP ACL: %s/%u %u\n", inet_ntoa(ip_acl_curr->addr), prefix_from_mask(ip_acl_curr->mask), ip_acl_curr->addr.s_addr);
  539. ip_acl_curr = ip_acl_curr->next;
  540. }
  541. while (dns_acl_curr != NULL) {
  542. printf("DNS ACL: %s\n", dns_acl_curr->domain);
  543. dns_acl_curr = dns_acl_curr->next;
  544. }
  545. }