acl.c 17 KB

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