PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/serefpolicy-3.11.0/support/fc_sort.c

#
C | 558 lines | 290 code | 111 blank | 157 comment | 73 complexity | 160bcb5a181e2fe5abe649c8e383b4a4 MD5 | raw file
Possible License(s): GPL-2.0
  1. /* Copyright 2005, Tresys Technology
  2. *
  3. * Some parts of this came from matchpathcon.c in libselinux
  4. */
  5. /* PURPOSE OF THIS PROGRAM
  6. * The original setfiles sorting algorithm did not take into
  7. * account regular expression specificity. With the current
  8. * strict and targeted policies this is not an issue because
  9. * the file contexts are partially hand sorted and concatenated
  10. * in the right order so that the matches are generally correct.
  11. * The way reference policy and loadable policy modules handle
  12. * file contexts makes them come out in an unpredictable order
  13. * and therefore setfiles (or this standalone tool) need to sort
  14. * the regular expressions in a deterministic and stable way.
  15. */
  16. #define BUF_SIZE 4096;
  17. #define _GNU_SOURCE
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <ctype.h>
  22. typedef unsigned char bool_t;
  23. /* file_context_node
  24. * A node used in a linked list of file contexts.c
  25. * Each node contains the regular expression, the type and
  26. * the context, as well as information about the regular
  27. * expression. The regular expression data (meta, stem_len
  28. * and str_len) can be filled in by using the fc_fill_data
  29. * function after the regular expression has been loaded.
  30. * next points to the next node in the linked list.
  31. */
  32. typedef struct file_context_node {
  33. char *path;
  34. char *file_type;
  35. char *context;
  36. bool_t meta;
  37. int stem_len;
  38. int str_len;
  39. struct file_context_node *next;
  40. } file_context_node_t;
  41. void file_context_node_destroy(file_context_node_t *x)
  42. {
  43. free(x->path);
  44. free(x->file_type);
  45. free(x->context);
  46. }
  47. /* file_context_bucket
  48. * A node used in a linked list of buckets that contain
  49. * file_context_node's.
  50. * Each node contains a pointer to a file_context_node which
  51. * is the header of its linked list. This linked list is the
  52. * content of this bucket.
  53. * next points to the next bucket in the linked list.
  54. */
  55. typedef struct file_context_bucket {
  56. file_context_node_t *data;
  57. struct file_context_bucket *next;
  58. } file_context_bucket_t;
  59. /* fc_compare
  60. * Compares two file contexts' regular expressions and returns:
  61. * -1 if a is less specific than b
  62. * 0 if a and be are equally specific
  63. * 1 if a is more specific than b
  64. * The comparison is based on the following statements,
  65. * in order from most important to least important, given a and b:
  66. * If a is a regular expression and b is not,
  67. * -> a is less specific than b.
  68. * If a's stem length is shorter than b's stem length,
  69. * -> a is less specific than b.
  70. * If a's string length is shorter than b's string length,
  71. * -> a is less specific than b.
  72. * If a does not have a specified type and b does not,
  73. * -> a is less specific than b.
  74. */
  75. int fc_compare(file_context_node_t *a, file_context_node_t *b)
  76. {
  77. /* Check to see if either a or b have meta characters
  78. * and the other doesn't. */
  79. if (a->meta && !b->meta)
  80. return -1;
  81. if (b->meta && !a->meta)
  82. return 1;
  83. /* Check to see if either a or b have a shorter stem
  84. * length than the other. */
  85. if (a->stem_len < b->stem_len)
  86. return -1;
  87. if (b->stem_len < a->stem_len)
  88. return 1;
  89. /* Check to see if either a or b have a shorter string
  90. * length than the other. */
  91. if (a->str_len < b->str_len)
  92. return -1;
  93. if (b->str_len < a->str_len)
  94. return 1;
  95. /* Check to see if either a or b has a specified type
  96. * and the other doesn't. */
  97. if (!a->file_type && b->file_type)
  98. return -1;
  99. if (!b->file_type && a->file_type)
  100. return 1;
  101. /* If none of the above conditions were satisfied,
  102. * then a and b are equally specific. */
  103. return 0;
  104. }
  105. /* fc_merge
  106. * Merges two sorted file context linked lists into one
  107. * sorted one.
  108. * Pass two lists a and b, and after the completion of fc_merge,
  109. * the final list is contained in a, and b is empty.
  110. */
  111. file_context_node_t *fc_merge(file_context_node_t *a,
  112. file_context_node_t *b)
  113. {
  114. file_context_node_t *a_current;
  115. file_context_node_t *b_current;
  116. file_context_node_t *temp;
  117. file_context_node_t *jumpto;
  118. /* If a is a empty list, and b is not,
  119. * set a as b and proceed to the end. */
  120. if (!a && b)
  121. a = b;
  122. /* If b is an empty list, leave a as it is. */
  123. else if (!b) {
  124. } else {
  125. /* Make it so the list a has the lesser
  126. * first element always. */
  127. if (fc_compare(a, b) == 1) {
  128. temp = a;
  129. a = b;
  130. b = temp;
  131. }
  132. a_current = a;
  133. b_current = b;
  134. /* Merge by inserting b's nodes in between a's nodes. */
  135. while (a_current->next && b_current) {
  136. jumpto = a_current->next;
  137. /* Insert b's nodes in between the current a node
  138. * and the next a node.*/
  139. while (b_current && a_current->next &&
  140. fc_compare(a_current->next,
  141. b_current) != -1) {
  142. temp = a_current->next;
  143. a_current->next = b_current;
  144. b_current = b_current->next;
  145. a_current->next->next = temp;
  146. a_current = a_current->next;
  147. }
  148. /* Skip all the inserted node from b to the
  149. * next node in the original a. */
  150. a_current = jumpto;
  151. }
  152. /* if there is anything left in b to be inserted,
  153. put it on the end */
  154. if (b_current) {
  155. a_current->next = b_current;
  156. }
  157. }
  158. return a;
  159. }
  160. /* fc_merge_sort
  161. * Sorts file contexts from least specific to more specific.
  162. * The bucket linked list is passed and after the completion
  163. * of the fc_merge_sort function, there is only one bucket
  164. * (pointed to by master) that contains a linked list
  165. * of all the file contexts, in sorted order.
  166. * Explanation of the algorithm:
  167. * The algorithm implemented in fc_merge_sort is an iterative
  168. * implementation of merge sort.
  169. * At first, each bucket has a linked list of file contexts
  170. * that are 1 element each.
  171. * Each pass, each odd numbered bucket is merged into the bucket
  172. * before it. This halves the number of buckets each pass.
  173. * It will continue passing over the buckets (as described above)
  174. * until there is only one bucket left, containing the list of
  175. * file contexts, sorted.
  176. */
  177. void fc_merge_sort(file_context_bucket_t *master)
  178. {
  179. file_context_bucket_t *current;
  180. file_context_bucket_t *temp;
  181. /* Loop until master is the only bucket left
  182. * so that this will stop when master contains
  183. * the sorted list. */
  184. while (master->next) {
  185. current = master;
  186. /* This loop merges buckets two-by-two. */
  187. while (current) {
  188. if (current->next) {
  189. current->data =
  190. fc_merge(current->data,
  191. current->next->data);
  192. temp = current->next;
  193. current->next = current->next->next;
  194. free(temp);
  195. }
  196. current = current->next;
  197. }
  198. }
  199. }
  200. /* fc_fill_data
  201. * This processes a regular expression in a file context
  202. * and sets the data held in file_context_node, namely
  203. * meta, str_len and stem_len.
  204. * The following changes are made to fc_node after the
  205. * the completion of the function:
  206. * fc_node->meta = 1 if path has a meta character, 0 if not.
  207. * fc_node->str_len = The string length of the entire path
  208. * fc_node->stem_len = The number of characters up until
  209. * the first meta character.
  210. */
  211. void fc_fill_data(file_context_node_t *fc_node)
  212. {
  213. int c = 0;
  214. fc_node->meta = 0;
  215. fc_node->stem_len = 0;
  216. fc_node->str_len = 0;
  217. /* Process until the string termination character
  218. * has been reached.
  219. * Note: this while loop has been adapted from
  220. * spec_hasMetaChars in matchpathcon.c from
  221. * libselinux-1.22. */
  222. while (fc_node->path[c] != '\0') {
  223. switch (fc_node->path[c]) {
  224. case '.':
  225. case '^':
  226. case '$':
  227. case '?':
  228. case '*':
  229. case '+':
  230. case '|':
  231. case '[':
  232. case '(':
  233. case '{':
  234. /* If a meta character is found,
  235. * set meta to one */
  236. fc_node->meta = 1;
  237. break;
  238. case '\\':
  239. /* If a escape character is found,
  240. * skip the next character. */
  241. c++;
  242. default:
  243. /* If no meta character has been found yet,
  244. * add one to the stem length. */
  245. if (!fc_node->meta)
  246. fc_node->stem_len++;
  247. break;
  248. }
  249. fc_node->str_len++;
  250. c++;
  251. }
  252. }
  253. /* main
  254. * This program takes in two arguments, the input filename and the
  255. * output filename. The input file should be syntactically correct.
  256. * Overall what is done in the main is read in the file and store each
  257. * line of code, sort it, then output it to the output file.
  258. */
  259. int main(int argc, char *argv[])
  260. {
  261. int lines;
  262. size_t start, finish, regex_len, context_len;
  263. size_t line_len, buf_len, i, j;
  264. char *input_name, *output_name, *line_buf;
  265. file_context_node_t *temp;
  266. file_context_node_t *head;
  267. file_context_node_t *current;
  268. file_context_bucket_t *master;
  269. file_context_bucket_t *bcurrent;
  270. FILE *in_file, *out_file;
  271. /* Check for the correct number of command line arguments. */
  272. if (argc != 3) {
  273. fprintf(stderr, "Usage: %s <infile> <outfile>\n",argv[0]);
  274. return 1;
  275. }
  276. input_name = argv[1];
  277. output_name = argv[2];
  278. i = j = lines = 0;
  279. /* Open the input file. */
  280. if (!(in_file = fopen(input_name, "r"))) {
  281. fprintf(stderr, "Error: failure opening input file for read.\n");
  282. return 1;
  283. }
  284. /* Initialize the head of the linked list. */
  285. head = current = (file_context_node_t*)malloc(sizeof(file_context_node_t));
  286. /* Parse the file into a file_context linked list. */
  287. line_buf = NULL;
  288. while ( getline(&line_buf, &buf_len, in_file) != -1 ){
  289. line_len = strlen(line_buf);
  290. if( line_len == 0 || line_len == 1)
  291. continue;
  292. /* Get rid of whitespace from the front of the line. */
  293. for (i = 0; i < line_len; i++) {
  294. if (!isspace(line_buf[i]))
  295. break;
  296. }
  297. if (i >= line_len)
  298. continue;
  299. /* Check if the line isn't empty and isn't a comment */
  300. if (line_buf[i] == '#')
  301. continue;
  302. /* We have a valid line - allocate a new node. */
  303. temp = (file_context_node_t *)malloc(sizeof(file_context_node_t));
  304. if (!temp) {
  305. fprintf(stderr, "Error: failure allocating memory.\n");
  306. return 1;
  307. }
  308. temp->next = NULL;
  309. memset(temp, 0, sizeof(file_context_node_t));
  310. /* Parse out the regular expression from the line. */
  311. start = i;
  312. while (i < line_len && (!isspace(line_buf[i])))
  313. i++;
  314. finish = i;
  315. regex_len = finish - start;
  316. if (regex_len == 0) {
  317. file_context_node_destroy(temp);
  318. free(temp);
  319. continue;
  320. }
  321. temp->path = (char*)strndup(&line_buf[start], regex_len);
  322. if (!temp->path) {
  323. file_context_node_destroy(temp);
  324. free(temp);
  325. fprintf(stderr, "Error: failure allocating memory.\n");
  326. return 1;
  327. }
  328. /* Get rid of whitespace after the regular expression. */
  329. for (; i < line_len; i++) {
  330. if (!isspace(line_buf[i]))
  331. break;
  332. }
  333. if (i == line_len) {
  334. file_context_node_destroy(temp);
  335. free(temp);
  336. continue;
  337. }
  338. /* Parse out the type from the line (if it
  339. * is there). */
  340. if (line_buf[i] == '-') {
  341. temp->file_type = (char *)malloc(sizeof(char) * 3);
  342. if (!(temp->file_type)) {
  343. fprintf(stderr, "Error: failure allocating memory.\n");
  344. return 1;
  345. }
  346. if( i + 2 >= line_len ) {
  347. file_context_node_destroy(temp);
  348. free(temp);
  349. continue;
  350. }
  351. /* Fill the type into the array. */
  352. temp->file_type[0] = line_buf[i];
  353. temp->file_type[1] = line_buf[i + 1];
  354. i += 2;
  355. temp->file_type[2] = 0;
  356. /* Get rid of whitespace after the type. */
  357. for (; i < line_len; i++) {
  358. if (!isspace(line_buf[i]))
  359. break;
  360. }
  361. if (i == line_len) {
  362. file_context_node_destroy(temp);
  363. free(temp);
  364. continue;
  365. }
  366. }
  367. /* Parse out the context from the line. */
  368. start = i;
  369. while (i < line_len && (!isspace(line_buf[i])))
  370. i++;
  371. finish = i;
  372. context_len = finish - start;
  373. temp->context = (char*)strndup(&line_buf[start], context_len);
  374. if (!temp->context) {
  375. file_context_node_destroy(temp);
  376. free(temp);
  377. fprintf(stderr, "Error: failure allocating memory.\n");
  378. return 1;
  379. }
  380. /* Set all the data about the regular
  381. * expression. */
  382. fc_fill_data(temp);
  383. /* Link this line of code at the end of
  384. * the linked list. */
  385. current->next = temp;
  386. current = current->next;
  387. lines++;
  388. free(line_buf);
  389. line_buf = NULL;
  390. }
  391. fclose(in_file);
  392. /* Create the bucket linked list from the earlier linked list. */
  393. current = head->next;
  394. bcurrent = master =
  395. (file_context_bucket_t *)
  396. malloc(sizeof(file_context_bucket_t));
  397. /* Go until all the nodes have been put in individual buckets. */
  398. while (current) {
  399. /* Copy over the file context line into the bucket. */
  400. bcurrent->data = current;
  401. current = current->next;
  402. /* Detatch the node in the bucket from the old list. */
  403. bcurrent->data->next = NULL;
  404. /* If there should be another bucket, put one at the end. */
  405. if (current) {
  406. bcurrent->next =
  407. (file_context_bucket_t *)
  408. malloc(sizeof(file_context_bucket_t));
  409. if (!(bcurrent->next)) {
  410. printf
  411. ("Error: failure allocating memory.\n");
  412. return -1;
  413. }
  414. /* Make sure the new bucket thinks it's the end of the
  415. * list. */
  416. bcurrent->next->next = NULL;
  417. bcurrent = bcurrent->next;
  418. }
  419. }
  420. /* Sort the bucket list. */
  421. fc_merge_sort(master);
  422. /* Open the output file. */
  423. if (!(out_file = fopen(argv[2], "w"))) {
  424. printf("Error: failure opening output file for write.\n");
  425. return -1;
  426. }
  427. /* Output the sorted file_context linked list to the output file. */
  428. current = master->data;
  429. while (current) {
  430. /* Output the path. */
  431. fprintf(out_file, "%s\t\t", current->path);
  432. /* Output the type, if there is one. */
  433. if (current->file_type) {
  434. fprintf(out_file, "%s\t", current->file_type);
  435. }
  436. /* Output the context. */
  437. fprintf(out_file, "%s\n", current->context);
  438. /* Remove the node. */
  439. temp = current;
  440. current = current->next;
  441. file_context_node_destroy(temp);
  442. free(temp);
  443. }
  444. free(master);
  445. fclose(out_file);
  446. return 0;
  447. }