1/*
2 * Copyright 2011 Tresys Technology, LLC. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 *    1. Redistributions of source code must retain the above copyright notice,
8 *       this list of conditions and the following disclaimer.
9 *
10 *    2. Redistributions in binary form must reproduce the above copyright notice,
11 *       this list of conditions and the following disclaimer in the documentation
12 *       and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``AS IS'' AND ANY EXPRESS
15 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
17 * EVENT SHALL TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
22 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * The views and conclusions contained in the software and documentation are those
26 * of the authors and should not be interpreted as representing official policies,
27 * either expressed or implied, of Tresys Technology, LLC.
28 */
29
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <stdint.h>
34#include <sepol/errcodes.h>
35
36#include "cil_internal.h"
37#include "cil_log.h"
38#include "cil_mem.h"
39#include "cil_tree.h"
40#include "cil_lexer.h"
41#include "cil_strpool.h"
42#include "cil_stack.h"
43
44char *CIL_KEY_HLL_LMS;
45char *CIL_KEY_HLL_LMX;
46char *CIL_KEY_HLL_LME;
47
48struct hll_info {
49	int hll_lineno;
50	int hll_expand;
51};
52
53static void push_hll_info(struct cil_stack *stack, int hll_lineno, int hll_expand)
54{
55	struct hll_info *new = cil_malloc(sizeof(*new));
56
57	new->hll_lineno = hll_lineno;
58	new->hll_expand = hll_expand;
59
60	cil_stack_push(stack, CIL_NONE, new);
61}
62
63static void pop_hll_info(struct cil_stack *stack, int *hll_lineno, int *hll_expand)
64{
65	struct cil_stack_item *curr = cil_stack_pop(stack);
66	struct cil_stack_item *prev = cil_stack_peek(stack);
67	struct hll_info *old;
68
69	free(curr->data);
70
71	if (!prev) {
72		*hll_lineno = -1;
73		*hll_expand = -1;
74	} else {
75		old = prev->data;
76		*hll_lineno = old->hll_lineno;
77		*hll_expand = old->hll_expand;
78	}
79}
80
81static void create_node(struct cil_tree_node **node, struct cil_tree_node *current, int line, int hll_line, void *value)
82{
83	cil_tree_node_init(node);
84	(*node)->parent = current;
85	(*node)->flavor = CIL_NODE;
86	(*node)->line = line;
87	(*node)->hll_line = hll_line;
88	(*node)->data = value;
89}
90
91static void insert_node(struct cil_tree_node *node, struct cil_tree_node *current)
92{
93	if (current->cl_head == NULL) {
94		current->cl_head = node;
95	} else {
96		current->cl_tail->next = node;
97	}
98	current->cl_tail = node;
99}
100
101static int add_hll_linemark(struct cil_tree_node **current, int *hll_lineno, int *hll_expand, struct cil_stack *stack, char *path)
102{
103	char *hll_type;
104	struct cil_tree_node *node;
105	struct token tok;
106	char *hll_file;
107	char *end = NULL;
108
109	cil_lexer_next(&tok);
110	hll_type = cil_strpool_add(tok.value);
111	if (hll_type == CIL_KEY_HLL_LME) {
112		if (cil_stack_is_empty(stack)) {
113			cil_log(CIL_ERR, "Line mark end without start\n");
114			goto exit;
115		}
116		pop_hll_info(stack, hll_lineno, hll_expand);
117		*current = (*current)->parent;
118	} else {
119		create_node(&node, *current, tok.line, *hll_lineno, NULL);
120		insert_node(node, *current);
121		*current = node;
122
123		create_node(&node, *current, tok.line, *hll_lineno, CIL_KEY_SRC_INFO);
124		insert_node(node, *current);
125
126		create_node(&node, *current, tok.line, *hll_lineno, CIL_KEY_SRC_HLL);
127		insert_node(node, *current);
128
129		if (hll_type == CIL_KEY_HLL_LMS) {
130			*hll_expand = 0;
131		} else if (hll_type == CIL_KEY_HLL_LMX) {
132			*hll_expand = 1;
133		} else {
134			cil_log(CIL_ERR, "Invalid line mark syntax\n");
135			goto exit;
136		}
137
138		cil_lexer_next(&tok);
139		if (tok.type != SYMBOL) {
140			cil_log(CIL_ERR, "Invalid line mark syntax\n");
141			goto exit;
142		}
143		*hll_lineno = strtol(tok.value, &end, 10);
144		if (errno == ERANGE || *end != '\0') {
145			cil_log(CIL_ERR, "Problem parsing line number for line mark\n");
146			goto exit;
147		}
148
149		push_hll_info(stack, *hll_lineno, *hll_expand);
150
151		cil_lexer_next(&tok);
152		if (tok.type != SYMBOL && tok.type != QSTRING) {
153			cil_log(CIL_ERR, "Invalid line mark syntax\n");
154			goto exit;
155		}
156
157		if (tok.type == QSTRING) {
158			tok.value[strlen(tok.value) - 1] = '\0';
159			tok.value = tok.value+1;
160		}
161
162		hll_file = cil_strpool_add(tok.value);
163
164		create_node(&node, *current, tok.line, *hll_lineno, hll_file);
165		insert_node(node, *current);
166	}
167
168	cil_lexer_next(&tok);
169	if (tok.type != NEWLINE) {
170		cil_log(CIL_ERR, "Invalid line mark syntax\n");
171		goto exit;
172	}
173
174	return SEPOL_OK;
175
176exit:
177	cil_log(CIL_ERR, "Problem with high-level line mark at line %d of %s\n", tok.line, path);
178	return SEPOL_ERR;
179}
180
181static void add_cil_path(struct cil_tree_node **current, char *path)
182{
183	struct cil_tree_node *node;
184
185	create_node(&node, *current, 0, 0, NULL);
186	insert_node(node, *current);
187	*current = node;
188
189	create_node(&node, *current, 0, 0, CIL_KEY_SRC_INFO);
190	insert_node(node, *current);
191
192	create_node(&node, *current, 0, 0, CIL_KEY_SRC_CIL);
193	insert_node(node, *current);
194
195	create_node(&node, *current, 0, 0, path);
196	insert_node(node, *current);
197}
198
199int cil_parser(char *_path, char *buffer, uint32_t size, struct cil_tree **parse_tree)
200{
201
202	int paren_count = 0;
203
204	struct cil_tree *tree = NULL;
205	struct cil_tree_node *node = NULL;
206	struct cil_tree_node *current = NULL;
207	char *path = cil_strpool_add(_path);
208	struct cil_stack *stack;
209	int hll_lineno = -1;
210	int hll_expand = -1;
211	struct token tok;
212	int rc = SEPOL_OK;
213
214	CIL_KEY_HLL_LMS = cil_strpool_add("lms");
215	CIL_KEY_HLL_LMX = cil_strpool_add("lmx");
216	CIL_KEY_HLL_LME = cil_strpool_add("lme");
217
218	cil_stack_init(&stack);
219
220	cil_lexer_setup(buffer, size);
221
222	tree = *parse_tree;
223	current = tree->root;
224
225	add_cil_path(&current, path);
226
227	do {
228		cil_lexer_next(&tok);
229		switch (tok.type) {
230		case HLL_LINEMARK:
231			rc = add_hll_linemark(&current, &hll_lineno, &hll_expand, stack, path);
232			if (rc != SEPOL_OK) {
233				goto exit;
234			}
235			break;
236		case OPAREN:
237			paren_count++;
238
239			create_node(&node, current, tok.line, hll_lineno, NULL);
240			insert_node(node, current);
241			current = node;
242			break;
243		case CPAREN:
244			paren_count--;
245			if (paren_count < 0) {
246				cil_log(CIL_ERR, "Close parenthesis without matching open at line %d of %s\n", tok.line, path);
247				goto exit;
248			}
249			current = current->parent;
250			break;
251		case QSTRING:
252			tok.value[strlen(tok.value) - 1] = '\0';
253			tok.value = tok.value+1;
254			/* FALLTHRU */
255		case SYMBOL:
256			if (paren_count == 0) {
257				cil_log(CIL_ERR, "Symbol not inside parenthesis at line %d of %s\n", tok.line, path);
258				goto exit;
259			}
260
261			create_node(&node, current, tok.line, hll_lineno, cil_strpool_add(tok.value));
262			insert_node(node, current);
263			break;
264		case NEWLINE :
265			if (!hll_expand) {
266				hll_lineno++;
267			}
268			break;
269		case COMMENT:
270			while (tok.type != NEWLINE && tok.type != END_OF_FILE) {
271				cil_lexer_next(&tok);
272			}
273			if (!hll_expand) {
274				hll_lineno++;
275			}
276			if (tok.type != END_OF_FILE) {
277				break;
278			}
279			/* FALLTHRU */
280			// Fall through if EOF
281		case END_OF_FILE:
282			if (paren_count > 0) {
283				cil_log(CIL_ERR, "Open parenthesis without matching close at line %d of %s\n", tok.line, path);
284				goto exit;
285			}
286			if (!cil_stack_is_empty(stack)) {
287				cil_log(CIL_ERR, "High-level language line marker start without close at line %d of %s\n", tok.line, path);
288				goto exit;
289			}
290			break;
291		case UNKNOWN:
292			cil_log(CIL_ERR, "Invalid token '%s' at line %d of %s\n", tok.value, tok.line, path);
293			goto exit;
294		default:
295			cil_log(CIL_ERR, "Unknown token type '%d' at line %d of %s\n", tok.type, tok.line, path);
296			goto exit;
297		}
298	}
299	while (tok.type != END_OF_FILE);
300
301	cil_lexer_destroy();
302
303	cil_stack_destroy(&stack);
304
305	*parse_tree = tree;
306
307	return SEPOL_OK;
308
309exit:
310	while (!cil_stack_is_empty(stack)) {
311		pop_hll_info(stack, &hll_lineno, &hll_expand);
312	}
313	cil_stack_destroy(&stack);
314
315	return SEPOL_ERR;
316}
317