forked from SELinuxProject/selinux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwriting-diffs-HOWTO
executable file
·351 lines (279 loc) · 13.5 KB
/
writing-diffs-HOWTO
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
Writing New Diff Modules for libpoldiff HOWTO
August 1, 2007
0. Introduction
libpoldiff is a library to be used in conjunction with libapol to find
"semantic" differences between policies. For the purposes of this
HOWTO, the term "semantic" refers to how the SELinux kernel would
enforce accesses. If two policies could ever be enforced differently
then they are defined to be semantically different.
libpoldiff operates by breaking a policy into various 'policy items'.
Examples of items are users, object classes, and type enforcement
rules. These items correspond to flags passed into the sediff
program. So as to be extensible, libpoldiff was designed to allow one
to add new diff modules (and hence additional flags to sediff). These
modules should operate independent of each other and without regard to
ordering of modules.
1. Library Overview
libpoldiff implements what we term "the generic diffing algorithm",
akin to a merge sort. The algorithm takes two ordered lists of items
and successively picks the first item from the lists. If the two
match, according to the items' comparison function, then they are
deemed the same. Otherwise the same ordering used to generate the
lists may also be used to determine if one was added or removed from
the policy.
As an example, consider a diff of users for two hypothetical SELinux
policies, "orig.policy" and "updated.policy". Within orig.policy are
users adam_u, charlie_u, and dave_u. updated.policy has the users adam_u,
bob_u, and charlie_u. The first step of the generic diffing algorithm
is to get the ordered list of items. Let the ordering algorithm be
alphabetical order; thus the lists would be:
orig.policy -> {adam_u, charlie_u, dave_u}
updated.policy -> {adam_u, bob_u, charlie_u}
The algorithm picks the first item from each list and compares them.
As that "adam_u" is the same as "adam_u", the algorithm accepts them
as the same and continues. The next two items are "charlie_u" and
"bob_u". The ordering functions finds "bob_u" to be "earlier" than
"charlie_u" (because it appears earlier alphabetically), "bob_u" is
marked as item added to updated.policy. The algorithm keeps
"charlie_u" from the first policy but advances the pointer for the
second list. These "charlie_u" are the same. The remaining type,
"dave_u", has no complement in the second list and is thus marked as
removed.
Of course, finding differences for users is not as simple as comparing
their names. In addition one must also examine the roles assigned to
them as well. Comparing names was a "shallow diff"; checking roles is
a "deep diff". The deep diff must look at all aspects of the two
items to determine if they are the same, modified,
modified-by-adding-a-type, or modified-by-removing-a-type. (At this
time the users' allowed MLS ranges and default range would also be
checked.)
To complicate things, consider the aspect of type remapping. Type
names may be changed between the policies; they could also be joined
into a new type and conversely split. Thus one must be careful how
the ordered lists of types are generated. The functions
type_map_lookup() and type_map_lookup_reverse() will prove essential.
2. Reporting Differences
If a difference was found, either via a shallow diff or a deep diff,
then an "item diff" struct must be created. If it the difference was
'added' or 'removed' then libpoldiff's poldiff_do_item_diff() will
call the item diff creation function. If instead the difference was
found within the deep diff comparison function then that function is
responsible for creating the item diff struct. The item diff struct
is used by each item's to_string() function to create a human-readable
report.
3. Complete Walkthrough
The following walkthrough describes the process for writing a diff for
items between the original and modified policies. The shallow
diff is to see if a type in the original policy exists in the modified
policy, with respect to the type map. The deep diff determines if the
types have the same set of attributes.
3a. Public Header
Create a new file, libpoldiff/include/poldiff/type_diff.h, to declare
publicly accessible functions. At least three functions must exist
here:
extern void poldiff_type_get_stats(poldiff_t *diff, size_t stats[5]);
extern apol_vector_t *poldiff_get_type_vector(poldiff_t *diff);
extern poldiff_form_e *poldiff_type_get_form(const void *type);
extern char *poldiff_type_to_string(poldiff_t *diff, const void *type);
(The reason for using a void * in poldiff_type_to_string() will become
apparant in section 3d.)
Also in this file, declare an opaque object to hold a single type
difference:
typedef struct poldiff_type poldiff_type_t;
Once a user gets a vector of poldiff_type_t objects, via
poldiff_get_type_vector(), he may want to do a number of things with
it. He could print it via poldiff_type_to_string() or get its form;
or just get the type's name, list of added attributes, or list of
removed attributes. Therefore add these three functions to
type_diff.h:
extern const char *poldiff_type_get_name(poldiff_type_t *type);
extern apol_vector_t *poldiff_type_get_added_attribs(poldiff_type_t *type);
extern apol_vector_t *poldiff_type_get_removed_attribs(poldiff_type_t *type);
As a convenience to developers, one should only need to #include the
public poldiff.h to pick up all diff modules. Modify
libpoldiff/include/poldiff/poldiff.h in the vicinity of line 74:
#include <poldiff/type_diff.h>
Finally, modify libpoldiff/include/poldiff/Makefile.am by adding an
entry for type_diff.h.
3b. Protected Header
There will be functions accessible only between library files (i.e.,
protected functions). To distinguish public functions from those that
are protected, do not prefix these with 'poldiff_'. Create a new
file, libpoldiff/src/type_internal.h, that declares protected
routines. libpoldiff requires these four functions to exist:
apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy);
int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item);
int type_comp(const void *x, const void *y, poldiff_t *diff);
int type_deep_diff(poldiff_t *diff, const void *x, const void *y);
Associated with the computed list of poldiff_type_t objects is a
summary structure. Check that libpoldiff/src/poldiff_internal.h has
declare a 'struct poldiff_type_summary', then add the following line
to the protected header:
typedef struct poldiff_type_summary poldiff_type_summary_t;
As with all other poldiff objects, you will need a constructor and a
destructor:
poldiff_type_summary_t *type_create(void);
void type_destroy(poldiff_type_summary_t **ts);
As a convenience to developers, one should only need to #include the
protected poldiff_internal.h to pick up all diff modules. Modify
libpoldiff/src/poldiff_internal.h in the vicinity of line 50:
#include "type_internal.h"
Finally, modify libpoldiff/src/Makefile.am by adding an entry for
type_internal.h.
3c. Implementing Functions
Create a new file, libpoldiff/src/type_diff.c, to implement all
public, protected, and any necessary private functions. First declare
the contents of the structures:
struct poldiff_type_summary {
size_t num_added;
size_t num_removed;
size_t num_modified;
apol_vector_t *diffs; /* vector of poldiff_type_t */
};
struct poldiff_type {
char *name;
poldiff_form_e form;
apol_vector_t *added_attribs; /* vector of char* */
apol_vector_t *removed_attribs; /* vector of char* */
};
The public functions are easy to write.
* poldiff_type_get_stats() fetches the fields
diff->type_diffs->num_added, diff->type_diffs->num_removed, and
diff->type_diffs->num_modified.
* poldiff_type_get_form() fetches an individual result's form. Note
that you must first cast the second parameter from void* to a
poldiff_type_t*, because this function operates upon items
returned by poldiff_get_type_vector().
* poldiff_type_to_string() returns an allocated string akin to
poldiff_user_to_string(). Note that you must first cast the
second parameter from void* to a poldiff_type_t*, because this
function operates upon items returned by
poldiff_get_type_vector().
* poldiff_get_type_vector() returns diff->type_diffs->diffs.
The rest of the public functions are accessors into a poldiff_type_t
object.
The protected functions are more difficult.
* type_create() and type_destroy() affect a poldiff_type_summary_t
object.
* The other protected functions are described in section 3e.
Finally, modify libpoldiff/src/Makefile.am by adding an entry for
type_diff.c.
3d. Adding Hooks to Diff Module
The main library now needs to create and destroy the
poldiff_type_summary_t object and to actually diff types. Make these
changes to libpoldiff/src/poldiff.c:
* Create a poldiff_type_summary_t object by calling type_create()
within poldiff_create().
* Destroy the summary by calling type_destroy() within
poldiff_destroy().
* To enforce that all diff modules have the requisite public and
protected functions, one must fulfill the requirements as given by
a poldiff_item_record, as defined on line 41. The first four
callbacks are satisfied by the first four public functions, the
remainder are met by protected functions. Thus add a new record
to the item_records[] array like so:
/* ... */
{
"type",
POLDIFF_DIFF_TYPES,
poldiff_type_get_stats,
poldiff_get_type_vector,
poldiff_type_get_form,
poldiff_type_to_string,
type_reset,
type_get_items,
type_comp,
type_new_diff,
type_deep_diff
},
/* ... */
Finally, for the public functions to be accessible through
libpoldiff.so, add this line to libpoldiff/src/libpoldiff.map under
the 'global' category:
poldiff_type_*;
3e. General Idea for Diffing Types
Rather than comparing qpol_type_t pointers from one policy to another,
it is more convenient to convert those pointers to "pseudo-type
values", which are represented as uint32_ts. These new values handle
the type mappings between policies. Whenever a difference is found,
convert those pseudo-type values back to the component qpol_type_t
pointers. With type splits and type joins, a single pseudo-type value
may map to multiple qpol_type_t pointers.
First look at type_get_items(). Its job is to return a sorted list of
unique items. Write it like this:
apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy)
{
get an iterator of types from the policy
allocate a new vector v
for each item in the iterator,
convert qpol_type_t* to uint32_t via type_map_lookup()
append that uint32_t to v
sort and unquify v
return v
}
type_map_lookup() needs a third parameter that says from which policy
the type originated. Use these lines to calculate the parameter:
if (policy == diff->orig_pol)
which_pol = POLDIFF_POLICY_ORIG;
else
which_pol = POLDIFF_POLICY_MOD;
Now that you have a vector of pseudo-type values, all further
functions will need to be in terms of these values. libpoldiff will
pass elements from the type_get_items() vector into your protected
functions. In this walkthrough you will thus need to cast all values
from void* to uint32_t because type_get_items() returned a vector of
uint32_ts. For example:
int type_comp(const void *x, const void *y,
poldiff_t *diff __attribute__((unused)))
{
uint32_t t1 = (uint32_t) x;
uint32_t t2 = (uint32_t) y;
return t1 - t2;
}
int type_deep_diff(poldiff_t *diff, const void *x, const void *y)
{
uint32_t t1 = (uint32_t) x;
uint32_t t2 = (uint32_t) y;
apol_vector_t *v1 = type_map_lookup_reverse(diff, t1,
POLDIFF_POLICY_ORIG);
apol_vector_t *v2 = type_map_lookup_reverse(diff, t2,
POLDIFF_POLICY_ORIG);
apol_vector_t *added_attribs, *removed_attribs;
let vector a1 = union of all attributes for all types in vector v1
let vector a2 = union of all attributes for all types in vector v2
sort and uniquify a1
sort and uniquify a2
for all attributes in a1 not in a2,
append to removed_attribs those attributes
for all attributes in a2 not in a1,
append to added_attribs those attributes
if removed_attribs is not empty or added_attribs is not empty,
then foreach type in v1,
create a new poldiff_type_t
set the poldiff_type_t's name to the type's name
set the form to POLDIFF_FORM_MODIFIED
clone added_attribs
clone removed_attribs
append the poldiff_type_t to diff->type_diffs->diffs
if no poldiff_type_t was created
return 0
else
return non-zero
}
int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item)
{
uint32_t t = (uint32_t) item;
apol_vector_t *v;
if (form == POLDIFF_FORM_REMOVED)
v = type_map_lookup(diff, t, POLDIFF_POLICY_ORIG);
else
v = type_map_lookup(diff, t, POLDIFF_POLICY_MOD);
foreach type in v,
create a new poldiff_type_t
set the poldiff_type_t's name to the type's name
set the form to form
append the poldiff_type_t to diff->type_diffs->diffs
}
One implementation of creating temporary vectors similar to
added_attribs and removed_attribs may be found at
libpoldiff/src/role_diff.c:role_deep_diff().