1 #include <getopt.h>
2 #include <sepol/policydb/expand.h>
3 
4 #include "typecmp.h"
5 
6 void typecmp_usage() {
7     fprintf(stderr, "\ttypecmp [-d|--diff] [-e|--equiv]\n");
8 }
9 
10 static int insert_type_rule(avtab_key_t * k, avtab_datum_t * d,
11                             struct avtab_node *type_rules)
12 {
13     struct avtab_node *p, *c, *n;
14 
15     for (p = type_rules, c = type_rules->next; c; p = c, c = c->next) {
16         /*
17          * Find the insertion point, keeping the list
18          * ordered by source type, then target type, then
19          * target class.
20          */
21         if (k->source_type < c->key.source_type)
22             break;
23         if (k->source_type == c->key.source_type &&
24             k->target_type < c->key.target_type)
25             break;
26         if (k->source_type == c->key.source_type &&
27             k->target_type == c->key.target_type &&
28             k->target_class <= c->key.target_class)
29             break;
30     }
31 
32     if (c &&
33         k->source_type == c->key.source_type &&
34         k->target_type == c->key.target_type &&
35         k->target_class == c->key.target_class) {
36         c->datum.data |= d->data;
37         return 0;
38     }
39 
40     /* Insert the rule */
41     n = malloc(sizeof(struct avtab_node));
42     if (!n) {
43         fprintf(stderr, "out of memory\n");
44         exit(1);
45     }
46 
47     n->key = *k;
48     n->datum = *d;
49     n->next = p->next;
50     p->next = n;
51     return 0;
52 }
53 
54 static int create_type_rules_helper(avtab_key_t * k, avtab_datum_t * d,
55                                     void *args)
56 {
57     struct avtab_node *type_rules = args;
58     avtab_key_t key;
59 
60     /*
61      * Insert the rule into the list for
62      * the source type.  The source type value
63      * is cleared as we want to compare against other type
64      * rules with different source types.
65      */
66     key = *k;
67     key.source_type = 0;
68     if (k->source_type == k->target_type) {
69         /* Clear target type as well; this is a self rule. */
70         key.target_type = 0;
71     }
72     if (insert_type_rule(&key, d, &type_rules[k->source_type - 1]))
73         return -1;
74 
75     if (k->source_type == k->target_type)
76         return 0;
77 
78     /*
79      * If the target type differs, then we also
80      * insert the rule into the list for the target
81      * type.  We clear the target type value so that
82      * we can compare against other type rules with
83      * different target types.
84      */
85     key = *k;
86     key.target_type = 0;
87     if (insert_type_rule(&key, d, &type_rules[k->target_type - 1]))
88         return -1;
89 
90     return 0;
91 }
92 
93 static int create_type_rules(avtab_key_t * k, avtab_datum_t * d, void *args)
94 {
95     if (k->specified & AVTAB_ALLOWED)
96         return create_type_rules_helper(k, d, args);
97     return 0;
98 }
99 
100 static int create_type_rules_cond(avtab_key_t * k, avtab_datum_t * d,
101                                   void *args)
102 {
103     if ((k->specified & (AVTAB_ALLOWED|AVTAB_ENABLED)) ==
104         (AVTAB_ALLOWED|AVTAB_ENABLED))
105         return create_type_rules_helper(k, d, args);
106     return 0;
107 }
108 
109 static void free_type_rules(struct avtab_node *l)
110 {
111     struct avtab_node *tmp;
112 
113     while (l) {
114         tmp = l;
115         l = l->next;
116         free(tmp);
117     }
118 }
119 
120 static int find_match(policydb_t *policydb, struct avtab_node *l1,
121                       int idx1, struct avtab_node *l2, int idx2)
122 {
123     struct avtab_node *c;
124     uint32_t perms1, perms2;
125 
126     for (c = l2; c; c = c->next) {
127         if (l1->key.source_type < c->key.source_type)
128             break;
129         if (l1->key.source_type == c->key.source_type &&
130             l1->key.target_type < c->key.target_type)
131             break;
132         if (l1->key.source_type == c->key.source_type &&
133             l1->key.target_type == c->key.target_type &&
134             l1->key.target_class <= c->key.target_class)
135             break;
136     }
137 
138     if (c &&
139         l1->key.source_type == c->key.source_type &&
140         l1->key.target_type == c->key.target_type &&
141         l1->key.target_class == c->key.target_class) {
142         perms1 = l1->datum.data & ~c->datum.data;
143         perms2 = c->datum.data & ~l1->datum.data;
144         if (perms1 || perms2) {
145             if (perms1)
146                 display_allow(policydb, &l1->key, idx1, perms1);
147             if (perms2)
148                 display_allow(policydb, &c->key, idx2, perms2);
149             printf("\n");
150             return 1;
151         }
152     }
153 
154     return 0;
155 }
156 
157 static int analyze_types(policydb_t * policydb, char diff, char equiv)
158 {
159     avtab_t exp_avtab, exp_cond_avtab;
160     struct avtab_node *type_rules, *l1, *l2;
161     struct type_datum *type;
162     size_t i, j;
163 
164     /*
165      * Create a list of access vector rules for each type
166      * from the access vector table.
167      */
168     type_rules = malloc(sizeof(struct avtab_node) * policydb->p_types.nprim);
169     if (!type_rules) {
170         fprintf(stderr, "out of memory\n");
171         exit(1);
172     }
173     memset(type_rules, 0, sizeof(struct avtab_node) * policydb->p_types.nprim);
174 
175     if (avtab_init(&exp_avtab) || avtab_init(&exp_cond_avtab)) {
176         fputs("out of memory\n", stderr);
177         free(type_rules);
178         return -1;
179     }
180 
181     if (expand_avtab(policydb, &policydb->te_avtab, &exp_avtab)) {
182         fputs("out of memory\n", stderr);
183         avtab_destroy(&exp_avtab);
184         free(type_rules);
185         return -1;
186     }
187 
188     if (expand_avtab(policydb, &policydb->te_cond_avtab, &exp_cond_avtab)) {
189         fputs("out of memory\n", stderr);
190         avtab_destroy(&exp_avtab); /*  */
191         free(type_rules);
192         return -1;
193     }
194 
195     if (avtab_map(&exp_avtab, create_type_rules, type_rules)) {
196         free(type_rules);
197         exit(1);
198     }
199 
200     if (avtab_map(&exp_cond_avtab, create_type_rules_cond, type_rules)) {
201         free(type_rules);
202         exit(1);
203     }
204 
205     avtab_destroy(&exp_avtab);
206     avtab_destroy(&exp_cond_avtab);
207 
208     /*
209      * Compare the type lists and identify similar types.
210      */
211     for (i = 0; i < policydb->p_types.nprim - 1; i++) {
212         if (!type_rules[i].next)
213             continue;
214         type = policydb->type_val_to_struct[i];
215         if (type->flavor) {
216             free_type_rules(type_rules[i].next);
217             type_rules[i].next = NULL;
218             continue;
219         }
220         for (j = i + 1; j < policydb->p_types.nprim; j++) {
221             type = policydb->type_val_to_struct[j];
222             if (type->flavor) {
223                 free_type_rules(type_rules[j].next);
224                 type_rules[j].next = NULL;
225                 continue;
226             }
227             for (l1 = type_rules[i].next, l2 = type_rules[j].next;
228                  l1 && l2; l1 = l1->next, l2 = l2->next) {
229                 if (l1->key.source_type != l2->key.source_type)
230                     break;
231                 if (l1->key.target_type != l2->key.target_type)
232                     break;
233                 if (l1->key.target_class != l2->key.target_class
234                     || l1->datum.data != l2->datum.data)
235                     break;
236             }
237             if (l1 || l2) {
238                 if (diff) {
239                     printf
240                         ("Types %s and %s differ, starting with:\n",
241                          policydb->p_type_val_to_name[i],
242                          policydb->p_type_val_to_name[j]);
243 
244                     if (l1 && l2) {
245                         if (find_match(policydb, l1, i, l2, j))
246                             continue;
247                         if (find_match(policydb, l2, j, l1, i))
248                             continue;
249                     }
250                     if (l1)
251                         display_allow(policydb, &l1->key, i, l1->datum.data);
252                     if (l2)
253                         display_allow(policydb, &l2->key, j, l2->datum.data);
254                     printf("\n");
255                 }
256                 continue;
257             }
258             free_type_rules(type_rules[j].next);
259             type_rules[j].next = NULL;
260             if (equiv) {
261                 printf("Types %s and %s are equivalent.\n",
262                        policydb->p_type_val_to_name[i],
263                        policydb->p_type_val_to_name[j]);
264             }
265         }
266         free_type_rules(type_rules[i].next);
267         type_rules[i].next = NULL;
268     }
269 
270     free(type_rules);
271     return 0;
272 }
273 
274 int typecmp_func (int argc, char **argv, policydb_t *policydb) {
275     char ch, diff = 0, equiv = 0;
276 
277     struct option typecmp_options[] = {
278         {"diff", no_argument, NULL, 'd'},
279         {"equiv", no_argument, NULL, 'e'},
280         {NULL, 0, NULL, 0}
281     };
282 
283     while ((ch = getopt_long(argc, argv, "de", typecmp_options, NULL)) != -1) {
284         switch (ch) {
285         case 'd':
286             diff = 1;
287             break;
288         case 'e':
289             equiv = 1;
290             break;
291         default:
292             USAGE_ERROR = true;
293             return -1;
294         }
295     }
296 
297     if (!(diff || equiv)) {
298         USAGE_ERROR = true;
299         return -1;
300     }
301     return analyze_types(policydb, diff, equiv);
302 }
303