1/*
2 * Media contexts backend for labeling system
3 *
4 * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
5 */
6
7#include <sys/stat.h>
8#include <string.h>
9#include <stdio.h>
10#include <stdio_ext.h>
11#include <ctype.h>
12#include <errno.h>
13#include <limits.h>
14#include "callbacks.h"
15#include "label_internal.h"
16
17/*
18 * Internals
19 */
20
21/* A context specification. */
22typedef struct spec {
23	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
24	char *key;		/* key string */
25	int matches;		/* number of matches made during operation */
26} spec_t;
27
28struct saved_data {
29	unsigned int nspec;
30	spec_t *spec_arr;
31};
32
33static int process_line(const char *path, char *line_buf, int pass,
34			unsigned lineno, struct selabel_handle *rec)
35{
36	struct saved_data *data = (struct saved_data *)rec->data;
37	int items;
38	char *buf_p;
39	char *key, *context;
40
41	buf_p = line_buf;
42	while (isspace(*buf_p))
43		buf_p++;
44	/* Skip comment lines and empty lines. */
45	if (*buf_p == '#' || *buf_p == 0)
46		return 0;
47	items = sscanf(line_buf, "%ms %ms ", &key, &context);
48	if (items < 2) {
49		selinux_log(SELINUX_WARNING,
50			  "%s:  line %u is missing fields, skipping\n", path,
51			  lineno);
52		if (items == 1)
53			free(key);
54		return 0;
55	}
56
57	if (pass == 1) {
58		data->spec_arr[data->nspec].key = key;
59		data->spec_arr[data->nspec].lr.ctx_raw = context;
60	}
61
62	data->nspec++;
63	if (pass == 0) {
64		free(key);
65		free(context);
66	}
67	return 0;
68}
69
70static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
71		unsigned n)
72{
73	FILE *fp;
74	struct saved_data *data = (struct saved_data *)rec->data;
75	const char *path = NULL;
76	char *line_buf = NULL;
77	size_t line_len = 0;
78	int status = -1;
79	unsigned int lineno, pass, maxnspec;
80	struct stat sb;
81
82	/* Process arguments */
83	while (n--)
84		switch(opts[n].type) {
85		case SELABEL_OPT_PATH:
86			path = opts[n].value;
87			break;
88		}
89
90	/* Open the specification file. */
91	if (!path)
92		path = selinux_media_context_path();
93	if ((fp = fopen(path, "r")) == NULL)
94		return -1;
95	__fsetlocking(fp, FSETLOCKING_BYCALLER);
96
97	if (fstat(fileno(fp), &sb) < 0)
98		return -1;
99	if (!S_ISREG(sb.st_mode)) {
100		errno = EINVAL;
101		return -1;
102	}
103	rec->spec_file = strdup(path);
104
105	/*
106	 * Perform two passes over the specification file.
107	 * The first pass counts the number of specifications and
108	 * performs simple validation of the input.  At the end
109	 * of the first pass, the spec array is allocated.
110	 * The second pass performs detailed validation of the input
111	 * and fills in the spec array.
112	 */
113	maxnspec = UINT_MAX / sizeof(spec_t);
114	for (pass = 0; pass < 2; pass++) {
115		lineno = 0;
116		data->nspec = 0;
117		while (getline(&line_buf, &line_len, fp) > 0 &&
118		       data->nspec < maxnspec) {
119			if (process_line(path, line_buf, pass, ++lineno, rec))
120				goto finish;
121		}
122		lineno = 0;
123
124		if (pass == 0) {
125			if (data->nspec == 0) {
126				status = 0;
127				goto finish;
128			}
129			data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
130			if (data->spec_arr == NULL)
131				goto finish;
132			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
133			maxnspec = data->nspec;
134			rewind(fp);
135		}
136	}
137	free(line_buf);
138
139	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
140	if (status)
141		goto finish;
142
143	digest_gen_hash(rec->digest);
144
145finish:
146	fclose(fp);
147	return status;
148}
149
150/*
151 * Backend interface routines
152 */
153static void close(struct selabel_handle *rec)
154{
155	struct saved_data *data = (struct saved_data *)rec->data;
156	struct spec *spec, *spec_arr = data->spec_arr;
157	unsigned int i;
158
159	for (i = 0; i < data->nspec; i++) {
160		spec = &spec_arr[i];
161		free(spec->key);
162		free(spec->lr.ctx_raw);
163		free(spec->lr.ctx_trans);
164	}
165
166	if (spec_arr)
167	    free(spec_arr);
168
169	free(data);
170}
171
172static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
173					 const char *key,
174					 int type __attribute__((unused)))
175{
176	struct saved_data *data = (struct saved_data *)rec->data;
177	spec_t *spec_arr = data->spec_arr;
178	unsigned int i;
179
180	for (i = 0; i < data->nspec; i++) {
181		if (!strncmp(spec_arr[i].key, key, strlen(key) + 1))
182			break;
183		if (!strncmp(spec_arr[i].key, "*", 2))
184			break;
185	}
186
187	if (i >= data->nspec) {
188		/* No matching specification. */
189		errno = ENOENT;
190		return NULL;
191	}
192
193	spec_arr[i].matches++;
194	return &spec_arr[i].lr;
195}
196
197static void stats(struct selabel_handle *rec)
198{
199	struct saved_data *data = (struct saved_data *)rec->data;
200	unsigned int i, total = 0;
201
202	for (i = 0; i < data->nspec; i++)
203		total += data->spec_arr[i].matches;
204
205	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
206		  data->nspec, total);
207}
208
209int selabel_media_init(struct selabel_handle *rec,
210				    const struct selinux_opt *opts,
211				    unsigned nopts)
212{
213	struct saved_data *data;
214
215	data = (struct saved_data *)malloc(sizeof(*data));
216	if (!data)
217		return -1;
218	memset(data, 0, sizeof(*data));
219
220	rec->data = data;
221	rec->func_close = &close;
222	rec->func_lookup = &lookup;
223	rec->func_stats = &stats;
224
225	return init(rec, opts, nopts);
226}
227