summaryrefslogtreecommitdiffstats
path: root/sediff/policy_view.c
blob: bc733a732fa10310c932a60f7c611cd10b0ccab5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/**
 *  @file
 *  Routines are responsible calculating a policy's statistics and
 *  displaying its source.
 *
 *  @author Jeremy A. Mowery jmowery@tresys.com
 *  @author Jason Tang jtang@tresys.com
 *  @author Randy Wicks rwicks@tresys.com
 *
 *  Copyright (C) 2005-2007 Tresys Technology, LLC
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <config.h>

#include "policy_view.h"
#include "utilgui.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <glade/glade.h>

struct policy_view
{
	toplevel_t *top;
	sediffx_policy_e which;
	GladeXML *xml;
	GtkTextBuffer *stats, *source;
	GtkTextView *stats_view, *source_view;
	GtkNotebook *notebook;
	GtkLabel *line_number;
	void *mmap_start;
	size_t mmap_length;
};

static const char *policy_view_widget_names[SEDIFFX_POLICY_NUM][3] = {
	{"toplevel policy_orig stats text", "toplevel policy_orig source text",
	 "toplevel policy_orig notebook"},
	{"toplevel policy_mod stats text", "toplevel policy_mod source text",
	 "toplevel policy_mod notebook"}
};

/**
 * As the user moves the cursor within the source policy text view
 * update the bottom label with the line number.  Once that view is
 * hidden, by flipping to a different tab, then clear the label.
 */
static void policy_view_notebook_on_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
						__attribute__ ((unused)), gpointer user_data)
{
	policy_view_t *view = (policy_view_t *) user_data;
	guint main_pagenum, view_pagenum;
	GtkTextMark *mark = NULL;
	GtkTextIter iter;

	main_pagenum = toplevel_get_notebook_page(view->top);
	view_pagenum = gtk_notebook_get_current_page(view->notebook);
	if (main_pagenum == 1 + view->which && view_pagenum == 1) {
		mark = gtk_text_buffer_get_insert(view->source);
		if (mark != NULL) {
			GString *string = g_string_new("");
			gtk_text_buffer_get_iter_at_mark(view->source, &iter, mark);
			g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1);
			gtk_label_set_text(view->line_number, string->str);
			g_string_free(string, TRUE);
		}
	} else {
		gtk_label_set_text(view->line_number, "");
	}
}

policy_view_t *policy_view_create(toplevel_t * top, sediffx_policy_e which)
{
	policy_view_t *view;
	GtkTextTag *mono_tag;
	int error = 0;

	if ((view = calloc(1, sizeof(*view))) == NULL) {
		error = errno;
		goto cleanup;
	}
	view->top = top;
	view->which = which;

	view->xml = glade_get_widget_tree(GTK_WIDGET(toplevel_get_window(view->top)));
	view->stats = gtk_text_buffer_new(NULL);
	view->source = gtk_text_buffer_new(NULL);
	mono_tag = gtk_text_buffer_create_tag(view->source, "mono-tag",
					      "style", PANGO_STYLE_NORMAL,
					      "weight", PANGO_WEIGHT_NORMAL, "family", "monospace", NULL);

	view->stats_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][0]));
	view->source_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][1]));
	assert(view->stats_view != NULL && view->source_view != NULL);
	gtk_text_view_set_buffer(view->stats_view, view->stats);
	gtk_text_view_set_buffer(view->source_view, view->source);

	view->notebook = GTK_NOTEBOOK(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][2]));
	view->line_number = GTK_LABEL(glade_xml_get_widget(view->xml, "toplevel line label"));
	assert(view->notebook != NULL && view->line_number != NULL);
	g_signal_connect_after(G_OBJECT(view->notebook), "event-after", G_CALLBACK(policy_view_notebook_on_event_after), view);

      cleanup:
	if (error != 0) {
		policy_view_destroy(&view);
		errno = error;
		return NULL;
	}
	return view;
}

void policy_view_destroy(policy_view_t ** view)
{
	if (view != NULL && *view != NULL) {
		free(*view);
		*view = NULL;
	}
}

/**
 * Update the policy stats text buffer (and hence its view) to show
 * statistics about the given policy.
 *
 * @param view View to update.
 * @param p New policy whose statistics to get.
 * @param path Path to the new policy.
 */
static void policy_view_stats_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path)
{
	GtkTextIter iter;
	gchar *contents = NULL;
	char *path_desc, *tmp = NULL;
	size_t num_classes = 0,
		num_commons = 0,
		num_perms = 0,
		num_types = 0,
		num_attribs = 0,
		num_allow = 0, num_auditallow = 0, num_dontaudit = 0,
		num_type_change = 0, num_type_member, num_type_trans = 0,
		num_roles = 0, num_roleallow = 0, num_role_trans = 0, num_users = 0, num_bools = 0;
	apol_vector_t *vec = NULL;
	qpol_iterator_t *i = NULL;
	qpol_policy_t *q = apol_policy_get_qpol(p);

	util_text_buffer_clear(view->stats);

	if ((path_desc = util_policy_path_to_full_string(path)) == NULL) {
		toplevel_ERR(view->top, "%s", strerror(errno));
		return;
	}
	tmp = apol_policy_get_version_type_mls_str(p);
	contents = g_strdup_printf("Policy: %s\n" "Policy Version & Type: %s\n", path_desc, tmp);
	free(path_desc);
	free(tmp);
	tmp = NULL;
	gtk_text_buffer_get_end_iter(view->stats, &iter);
	gtk_text_buffer_insert(view->stats, &iter, contents, -1);
	g_free(contents);

	apol_class_get_by_query(p, NULL, &vec);
	num_classes = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	apol_common_get_by_query(p, NULL, &vec);
	num_commons = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	apol_perm_get_by_query(p, NULL, &vec);
	num_perms = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	contents = g_strdup_printf("\nNumber of Classes and Permissions:\n"
				   "\tObject Classes: %zd\n"
				   "\tCommon Classes: %zd\n" "\tPermissions: %zd\n", num_classes, num_commons, num_perms);
	gtk_text_buffer_insert(view->stats, &iter, contents, -1);
	g_free(contents);

	apol_type_get_by_query(p, NULL, &vec);
	num_types = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	apol_attr_get_by_query(p, NULL, &vec);
	num_attribs = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	contents = g_strdup_printf("\nNumber of Types and Attributes:\n"
				   "\tTypes: %zd\n" "\tAttributes: %zd\n", num_types, num_attribs);
	gtk_text_buffer_insert(view->stats, &iter, contents, -1);
	g_free(contents);

	qpol_policy_get_avrule_iter(q, QPOL_RULE_ALLOW, &i);
	qpol_iterator_get_size(i, &num_allow);
	qpol_iterator_destroy(&i);

	qpol_policy_get_avrule_iter(q, QPOL_RULE_AUDITALLOW, &i);
	qpol_iterator_get_size(i, &num_auditallow);
	qpol_iterator_destroy(&i);

	qpol_policy_get_avrule_iter(q, QPOL_RULE_DONTAUDIT, &i);
	qpol_iterator_get_size(i, &num_dontaudit);
	qpol_iterator_destroy(&i);

	qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_CHANGE, &i);
	qpol_iterator_get_size(i, &num_type_change);
	qpol_iterator_destroy(&i);

	qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_MEMBER, &i);
	qpol_iterator_get_size(i, &num_type_member);
	qpol_iterator_destroy(&i);

	qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_TRANS, &i);
	qpol_iterator_get_size(i, &num_type_trans);
	qpol_iterator_destroy(&i);

	contents = g_strdup_printf("\nNumber of Rules:\n"
				   "\tallow: %zd\n"
				   "\tauditallow: %zd\n"
				   "\tdontaudit %zd\n"
				   "\tneverallow: not calculated\n"
				   "\ttype_change: %zd\n"
				   "\ttype_member: %zd\n"
				   "\ttype_transition: %zd\n",
				   num_allow, num_auditallow, num_dontaudit, num_type_change, num_type_member, num_type_trans);
	gtk_text_buffer_insert(view->stats, &iter, contents, -1);
	g_free(contents);

	apol_role_get_by_query(p, NULL, &vec);
	num_roles = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	qpol_policy_get_role_allow_iter(q, &i);
	qpol_iterator_get_size(i, &num_roleallow);
	qpol_iterator_destroy(&i);

	qpol_policy_get_role_trans_iter(q, &i);
	qpol_iterator_get_size(i, &num_roleallow);
	qpol_iterator_destroy(&i);

	contents = g_strdup_printf("\nNumber of Roles: %zd\n"
				   "\nNumber of RBAC Rules:\n"
				   "\tallow: %zd\n" "\trole_transition %zd\n", num_roles, num_roleallow, num_role_trans);
	gtk_text_buffer_insert(view->stats, &iter, contents, -1);
	g_free(contents);

	apol_user_get_by_query(p, NULL, &vec);
	num_users = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	apol_bool_get_by_query(p, NULL, &vec);
	num_bools = apol_vector_get_size(vec);
	apol_vector_destroy(&vec);

	contents = g_strdup_printf("\nNumber of Users: %zd\n" "\nNumber of Booleans: %zd\n", num_users, num_bools);
	gtk_text_buffer_insert(view->stats, &iter, contents, -1);
	g_free(contents);
}

/**
 * Attempt to load the primary policy into this view's source policy
 * buffer.  If the policy is not a source policy then show an
 * appropriate message.  Otherwise mmap() the policy's contents to the
 * buffer.
 *
 * @param view View whose source buffer to update.
 * @param policy Policy to show, or NULL if none loaded.
 * @param path Path to the policy.
 */
static void policy_view_source_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path)
{
	const char *primary_path;
	util_text_buffer_clear(view->source);

	/* clear out any old data */
	if (view->mmap_start != NULL) {
		munmap(view->mmap_start, view->mmap_length);
		view->mmap_start = NULL;
		view->mmap_length = 0;
	}
	if (p == NULL) {
		gtk_text_buffer_set_text(view->source, "No policy has been loaded.", -1);
		return;
	}
	primary_path = apol_policy_path_get_primary(path);
	if (!qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SOURCE)) {
		GString *string = g_string_new("");
		g_string_printf(string, "Policy file %s is not a source policy.", primary_path);
		gtk_text_buffer_set_text(view->source, string->str, -1);
		g_string_free(string, TRUE);
	} else {
		/* load the policy by mmap()ing the file */
		struct stat statbuf;
		int fd;

		if ((fd = open(primary_path, O_RDONLY)) < 0) {
			toplevel_ERR(view->top, "Could not open %s for reading: %s", primary_path, strerror(errno));
			return;
		}
		if (fstat(fd, &statbuf) < 0) {
			toplevel_ERR(view->top, "Could not stat %s: %s", primary_path, strerror(errno));
			close(fd);
			return;
		}

		view->mmap_length = statbuf.st_size;
		if ((view->mmap_start = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
			toplevel_ERR(view->top, "Could not mmap %s: %s", primary_path, strerror(errno));
			close(fd);
			view->mmap_start = NULL;
			return;
		}
		close(fd);
		gtk_text_buffer_set_text(view->source, view->mmap_start, view->mmap_length);
	}
}

void policy_view_update(policy_view_t * view, apol_policy_t * policy, apol_policy_path_t * path)
{
	policy_view_stats_update(view, policy, path);
	policy_view_source_update(view, policy, path);
}

void policy_view_show_policy_line(policy_view_t * view, unsigned long line)
{
	GtkTextTagTable *table = NULL;
	GtkTextIter iter, end_iter;
	GtkTextMark *mark = NULL;
	GString *string = g_string_new("");

	gtk_notebook_set_current_page(view->notebook, 1);

	/* when moving the buffer we must use marks to scroll because
	 * goto_line if called before the line height has been
	 * calculated can produce undesired results, in our case we
	 * get no scrolling at all */
	table = gtk_text_buffer_get_tag_table(view->source);
	gtk_text_buffer_get_start_iter(view->source, &iter);
	gtk_text_iter_set_line(&iter, line);
	gtk_text_buffer_get_start_iter(view->source, &end_iter);
	gtk_text_iter_set_line(&end_iter, line);
	while (!gtk_text_iter_ends_line(&end_iter)) {
		gtk_text_iter_forward_char(&end_iter);
	}

	mark = gtk_text_buffer_create_mark(view->source, "line-position", &iter, TRUE);
	assert(mark);
	gtk_text_view_scroll_to_mark(view->source_view, mark, 0.0, TRUE, 0.0, 0.5);

	/* destroying the mark and recreating is faster than doing a
	 * move on a mark that still exists, so we always destroy it
	 * once we're done */
	gtk_text_buffer_delete_mark(view->source, mark);
	gtk_text_view_set_cursor_visible(view->source_view, TRUE);
	gtk_text_buffer_place_cursor(view->source, &iter);
	gtk_text_buffer_select_range(view->source, &iter, &end_iter);

	gtk_container_set_focus_child(GTK_CONTAINER(view->notebook), GTK_WIDGET(view->source_view));

	g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1);
	gtk_label_set_text(view->line_number, string->str);
	g_string_free(string, TRUE);
}

GtkTextView *policy_view_get_text_view(policy_view_t * view)
{
	gint pagenum = gtk_notebook_get_current_page(view->notebook);
	if (pagenum == 0) {
		return view->stats_view;
	} else {
		return view->source_view;
	}
}