acl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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. addrtok = strtok_r(ipv6tmp, "/", &addrsave);
  246. if(inet_pton(AF_INET6, addrtok, &addr) <= 0) {
  247. syslog(LOG_ERR, "Invalid IPv6 address in ACL: %s\n", ipv6);
  248. free(ipv6tmp);
  249. return 0;
  250. }
  251. /* Check whether there is a netmask */
  252. addrtok = strtok_r(NULL, "/", &addrsave);
  253. if(NULL != addrtok) {
  254. /* If so, build a netmask */
  255. /* Get the number of bits in the mask */
  256. maskval = atoi(addrtok);
  257. if(maskval < 0 || maskval > 128) {
  258. syslog(LOG_ERR, "Invalid IPv6 netmask in ACL: %s\n", ipv6);
  259. free(ipv6tmp);
  260. return 0;
  261. }
  262. /* Initialize to zero */
  263. for(x = 0; x < nbytes; x++) {
  264. mask.s6_addr[x] = 0;
  265. }
  266. /* Set mask based on mask bits */
  267. byte = 0;
  268. bit = 7;
  269. while(maskval > 0) {
  270. mask.s6_addr[byte] |= 1 << bit;
  271. bit -= 1;
  272. if(bit < 0) {
  273. bit = 7;
  274. byte++;
  275. }
  276. maskval--;
  277. }
  278. }
  279. else {
  280. /* Otherwise, this is a single address */
  281. for(x = 0; x < nbytes; x++) {
  282. mask.s6_addr[x] = 0xFF;
  283. }
  284. }
  285. /* Add address to ip_acl list */
  286. ip_acl_curr = malloc(sizeof(*ip_acl_curr));
  287. if(NULL == ip_acl_curr) {
  288. syslog(LOG_ERR, "Memory allocation failed for ACL: %s\n", ipv6);
  289. return 0;
  290. }
  291. /* Save result in ACL ip list */
  292. ip_acl_curr->family = AF_INET6;
  293. for(x = 0; x < nbytes; x++) {
  294. ip_acl_curr->addr6.s6_addr[x] =
  295. addr.s6_addr[x] & mask.s6_addr[x];
  296. ip_acl_curr->mask6.s6_addr[x] = mask.s6_addr[x];
  297. }
  298. ip_acl_curr->next = NULL;
  299. if(NULL == ip_acl_head) {
  300. ip_acl_head = ip_acl_curr;
  301. }
  302. else {
  303. ip_acl_prev->next = ip_acl_curr;
  304. }
  305. ip_acl_prev = ip_acl_curr;
  306. free(ipv6tmp);
  307. return 1;
  308. }
  309. /*
  310. * Add domain to DNS ACL list
  311. * Domain will be added only if it has passed domain name check.
  312. *
  313. * In this case domain valid format is:
  314. * 1) Domain names must use only alphanumeric characters and dashes (-).
  315. * 2) Domain names mustn't begin or end with dashes (-).
  316. * 3) Domain names mustn't have more than 63 characters.
  317. *
  318. * Return:
  319. * 1 - for success
  320. * 0 - for failure
  321. *
  322. * 0 - alpha(-> 1), number(-> 1), dot(-> -1), dash(-> -1), all other(-> -1)
  323. * 1 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  324. * 2 - alpha(-> 3), number(-> 1), dot(-> -1), dash(-> -1), all other(-> -1)
  325. * 3 - alpha(-> 4), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  326. * 4 - alpha(-> 5), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  327. * 5 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  328. * 6 - alpha(-> 1), number(-> 1), dot(-> 2), dash(-> 6), all other(-> -1)
  329. * For real FQDN only 4 and 5 states are good for exit.
  330. * I don't check if top domain exists (com, ru and etc.)
  331. * But in real life NRPE could work in LAN,
  332. * with local domain zones like .local or with names like 'mars' added to /etc/hosts.
  333. * So 1 is good state too. And maybe this check is not necessary at all...
  334. */
  335. int add_domain_to_acl(char *domain) {
  336. int state = 0;
  337. int len = strlen(domain);
  338. int i, c;
  339. struct dns_acl *dns_acl_curr;
  340. if (len > 63)
  341. return 0;
  342. for (i = 0; i < len; i++) {
  343. c = domain[i];
  344. switch (isvalidchar(c)) {
  345. case 1:
  346. state = 1;
  347. break;
  348. case 2:
  349. switch (state) {
  350. case 0: case 1: case 5: case 6:
  351. state = 1;
  352. break;
  353. case 2: case 3: case 4:
  354. state++;
  355. break;
  356. }
  357. break;
  358. case 4:
  359. switch (state) {
  360. case 0: case 2:
  361. state = -1;
  362. break;
  363. default:
  364. state = 2;
  365. }
  366. break;
  367. case 6:
  368. switch (state) {
  369. case 0: case 2:
  370. state = -1;
  371. break;
  372. default:
  373. state = 6;
  374. }
  375. break;
  376. default:
  377. /* Not valid chars */
  378. return 0;
  379. }
  380. }
  381. /* Check exit code */
  382. switch (state) {
  383. case 1: case 4: case 5:
  384. /* Add name to domain ACL list */
  385. if ( (dns_acl_curr = malloc(sizeof(*dns_acl_curr))) == NULL) {
  386. syslog(LOG_ERR,"Can't allocate memory for ACL, malloc error\n");
  387. return 0;
  388. }
  389. strcpy(dns_acl_curr->domain, domain);
  390. dns_acl_curr->next = NULL;
  391. if (dns_acl_head == NULL)
  392. dns_acl_head = dns_acl_curr;
  393. else
  394. dns_acl_prev->next = dns_acl_curr;
  395. dns_acl_prev = dns_acl_curr;
  396. return 1;
  397. default:
  398. return 0;
  399. }
  400. }
  401. /* Checks connectiong host in ACL
  402. *
  403. * Returns:
  404. * 1 - on success
  405. * 0 - on failure
  406. */
  407. int is_an_allowed_host(int family, void *host)
  408. {
  409. struct ip_acl *ip_acl_curr = ip_acl_head;
  410. int nbytes;
  411. int x;
  412. struct dns_acl *dns_acl_curr = dns_acl_head;
  413. struct sockaddr_in *addr;
  414. struct sockaddr_in6 addr6;
  415. struct addrinfo *res, *ai;
  416. while (ip_acl_curr != NULL) {
  417. if(ip_acl_curr->family == family) {
  418. switch(ip_acl_curr->family) {
  419. case AF_INET:
  420. if((((struct in_addr *)host)->s_addr &
  421. ip_acl_curr->mask.s_addr) ==
  422. ip_acl_curr->addr.s_addr) {
  423. return 1;
  424. }
  425. break;
  426. case AF_INET6:
  427. nbytes = sizeof(ip_acl_curr->mask6.s6_addr) /
  428. sizeof(ip_acl_curr->mask6.s6_addr[0]);
  429. for(x = 0; x < nbytes; x++) {
  430. if((((struct in6_addr *)host)->s6_addr[x] &
  431. ip_acl_curr->mask6.s6_addr[x]) !=
  432. ip_acl_curr->addr6.s6_addr[x]) {
  433. break;
  434. }
  435. }
  436. if(x == nbytes) {
  437. /* All bytes in host's address pass the netmask mask */
  438. return 1;
  439. }
  440. break;
  441. }
  442. }
  443. ip_acl_curr = ip_acl_curr->next;
  444. }
  445. while(dns_acl_curr != NULL) {
  446. if (!getaddrinfo(dns_acl_curr->domain, NULL, NULL, &res)) {
  447. for (ai = res; ai; ai = ai->ai_next) {
  448. switch(ai->ai_family) {
  449. case AF_INET:
  450. addr = (struct sockaddr_in*)(ai->ai_addr);
  451. if (addr->sin_addr.s_addr == ((struct in_addr*)host)->s_addr)
  452. return 1;
  453. break;
  454. case AF_INET6:
  455. memcpy((char*)&addr6, ai->ai_addr, sizeof(addr6));
  456. if (!memcmp(&addr6.sin6_addr, &host, sizeof(addr6.sin6_addr)))
  457. return 1;
  458. break;
  459. }
  460. }
  461. dns_acl_curr = dns_acl_curr->next;
  462. }
  463. }
  464. return 0;
  465. }
  466. /* The trim() function takes a source string and copies it to the destination string,
  467. * stripped of leading and training whitespace. The destination string must be
  468. * allocated at least as large as the source string.
  469. */
  470. void trim( char *src, char *dest) {
  471. char *sptr, *dptr;
  472. for( sptr = src; isblank( *sptr) && *sptr; sptr++); /* Jump past leading spaces */
  473. for( dptr = dest; !isblank( *sptr) && *sptr; ) {
  474. *dptr = *sptr;
  475. sptr++;
  476. dptr++;
  477. }
  478. *dptr = '\0';
  479. return;
  480. }
  481. /* This function splits allowed_hosts to substrings with comma(,) as a delimiter.
  482. * It doesn't check validness of ACL record (add_ipv4_to_acl() and add_domain_to_acl() do),
  483. * just trims spaces from ACL records.
  484. * After this it sends ACL records to add_ipv4_to_acl() or add_domain_to_acl().
  485. */
  486. void parse_allowed_hosts(char *allowed_hosts) {
  487. char *hosts = strdup( allowed_hosts); /* Copy since strtok* modifies original */
  488. char *saveptr;
  489. char *tok;
  490. const char *delim = ",";
  491. char *trimmed_tok;
  492. tok = strtok_r( hosts, delim, &saveptr);
  493. while( tok) {
  494. trimmed_tok = malloc( sizeof( char) * ( strlen( tok) + 1));
  495. trim( tok, trimmed_tok);
  496. if( strlen( trimmed_tok) > 0) {
  497. if (!add_ipv4_to_acl(trimmed_tok) && !add_ipv6_to_acl(trimmed_tok)
  498. && !add_domain_to_acl(trimmed_tok)) {
  499. syslog(LOG_ERR,"Can't add to ACL this record (%s). Check allowed_hosts option!\n",trimmed_tok);
  500. }
  501. }
  502. free( trimmed_tok);
  503. tok = strtok_r(( char *)0, delim, &saveptr);
  504. }
  505. free( hosts);
  506. }
  507. /*
  508. * Converts mask in unsigned long format to two digit prefix
  509. */
  510. unsigned int prefix_from_mask(struct in_addr mask) {
  511. int prefix = 0;
  512. unsigned long bit = 1;
  513. int i;
  514. for (i = 0; i < 32; i++) {
  515. if (mask.s_addr & bit)
  516. prefix++;
  517. bit = bit << 1;
  518. }
  519. return (prefix);
  520. }
  521. /*
  522. * It shows all hosts in ACL lists
  523. */
  524. void show_acl_lists(void) {
  525. struct ip_acl *ip_acl_curr = ip_acl_head;
  526. struct dns_acl *dns_acl_curr = dns_acl_head;
  527. while (ip_acl_curr != NULL) {
  528. 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);
  529. ip_acl_curr = ip_acl_curr->next;
  530. }
  531. while (dns_acl_curr != NULL) {
  532. printf("DNS ACL: %s\n", dns_acl_curr->domain);
  533. dns_acl_curr = dns_acl_curr->next;
  534. }
  535. }