1#!/usr/bin/python
2# Copyright 2015-2016, Tresys Technology, LLC
3#
4# This file is part of SETools.
5#
6# SETools is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 2 of the License, or
9# (at your option) any later version.
10#
11# SETools is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with SETools.  If not, see <http://www.gnu.org/licenses/>.
18#
19
20from __future__ import print_function
21import setools
22import argparse
23import sys
24import logging
25from itertools import chain
26
27parser = argparse.ArgumentParser(
28    description="SELinux policy semantic difference tool.",
29    epilog="If no differences are selected, all differences will be printed.")
30parser.add_argument("POLICY1", help="Path to the first SELinux policy to diff.", nargs=1)
31parser.add_argument("POLICY2", help="Path to the second SELinux policy to diff.", nargs=1)
32parser.add_argument("--version", action="version", version=setools.__version__)
33parser.add_argument("--stats", action="store_true", help="Display only statistics.")
34parser.add_argument("-v", "--verbose", action="store_true",
35                    help="Print extra informational messages")
36parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
37
38comp = parser.add_argument_group("component differences")
39comp.add_argument("--common", action="store_true", help="Print common differences")
40comp.add_argument("-c", "--class", action="store_true", help="Print class differences",
41                  dest="class_")
42comp.add_argument("-t", "--type", action="store_true", help="Print type differences",
43                  dest="type_")
44comp.add_argument("-a", "--attribute", action="store_true", help="Print type attribute differences")
45comp.add_argument("-r", "--role", action="store_true", help="Print role differences")
46comp.add_argument("-u", "--user", action="store_true", help="Print user differences")
47comp.add_argument("-b", "--bool", action="store_true", help="Print Boolean differences",
48                  dest="bool_")
49comp.add_argument("--sensitivity", action="store_true", help="Print MLS sensitivity differences")
50comp.add_argument("--category", action="store_true", help="Print MLS category differences")
51comp.add_argument("--level", action="store_true", help="Print MLS level definition differences")
52
53terule = parser.add_argument_group("type enforcement rule differences")
54terule.add_argument("-A", "--allow", action="store_true", help="Print allow rule differences")
55terule.add_argument("--neverallow", action="store_true", help="Print neverallow rule differences")
56terule.add_argument("--auditallow", action="store_true", help="Print auditallow rule differences")
57terule.add_argument("--dontaudit", action="store_true", help="Print dontaudit rule differences")
58terule.add_argument("-T", "--type_trans", action="store_true",
59                    help="Print type_transition rule differences")
60terule.add_argument("--type_change", action="store_true", help="Print type_change rule differences")
61terule.add_argument("--type_member", action="store_true",
62                    help="Print type_member rule differences")
63
64rbacrule = parser.add_argument_group("RBAC rule differences")
65rbacrule.add_argument("--role_allow", action="store_true", help="Print role allow rule differences")
66rbacrule.add_argument("--role_trans", action="store_true",
67                      help="Print role_transition rule differences")
68
69mlsrule = parser.add_argument_group("MLS rule differences")
70mlsrule.add_argument("--range_trans", action="store_true",
71                     help="Print range_transition rule differences")
72
73labeling = parser.add_argument_group("labeling statement differences")
74labeling.add_argument("--initialsid", action="store_true", help="Print initial SID differences")
75labeling.add_argument("--fs_use", action="store_true", help="Print fs_use_* differences")
76labeling.add_argument("--genfscon", action="store_true", help="Print genfscon differences")
77labeling.add_argument("--netifcon", action="store_true", help="Print netifcon differences")
78labeling.add_argument("--nodecon", action="store_true", help="Print nodecon differences")
79labeling.add_argument("--portcon", action="store_true", help="Print portcon differences")
80
81other = parser.add_argument_group("other differences")
82other.add_argument("--default", action="store_true", help="Print default_* differences")
83other.add_argument("--property", action="store_true",
84                   help="Print policy property differences (handle_unknown, version, MLS)")
85other.add_argument("--polcap", action="store_true", help="Print policy capability differences")
86
87args = parser.parse_args()
88
89all_differences = not any((args.class_, args.common, args.type_, args.attribute, args.role,
90                           args.user, args.bool_, args.sensitivity, args.category, args.level,
91                           args.allow, args.neverallow, args.auditallow, args.dontaudit,
92                           args.type_trans, args.type_change, args.type_member, args.role_allow,
93                           args.role_trans, args.range_trans, args.initialsid, args.genfscon,
94                           args.netifcon, args.nodecon, args.portcon, args.fs_use, args.polcap,
95                           args.property, args.default))
96
97if args.debug:
98    logging.basicConfig(level=logging.DEBUG,
99                        format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
100elif args.verbose:
101    logging.basicConfig(level=logging.INFO, format='%(message)s')
102else:
103    logging.basicConfig(level=logging.WARNING, format='%(message)s')
104
105try:
106    p1 = setools.SELinuxPolicy(args.POLICY1[0])
107    p2 = setools.SELinuxPolicy(args.POLICY2[0])
108    diff = setools.PolicyDifference(p1, p2)
109
110    if all_differences or args.property:
111        print("Policy Properties ({0} Modified)".format(len(diff.modified_properties)))
112
113        if diff.modified_properties and not args.stats:
114            for name, added, removed in sorted(diff.modified_properties, key=lambda x: x.property):
115                print("      * {0} +{1} -{2}".format(name, added, removed))
116
117        print()
118
119    if all_differences or args.common:
120        if diff.added_commons or diff.removed_commons or diff.modified_commons or args.common:
121            print("Commons ({0} Added, {1} Removed, {2} Modified)".format(
122                len(diff.added_commons), len(diff.removed_commons), len(diff.modified_commons)))
123            if diff.added_commons and not args.stats:
124                print("   Added Commons: {0}".format(len(diff.added_commons)))
125                for c in sorted(diff.added_commons):
126                    print("      + {0}".format(c))
127            if diff.removed_commons and not args.stats:
128                print("   Removed Commons: {0}".format(len(diff.removed_commons)))
129                for c in sorted(diff.removed_commons):
130                    print("      - {0}".format(c))
131            if diff.modified_commons and not args.stats:
132                print("   Modified Commons: {0}".format(len(diff.modified_commons)))
133                for name, mod in sorted(diff.modified_commons.items()):
134                    change = []
135                    if mod.added_perms:
136                        change.append("{0} Added permissions".format(len(mod.added_perms)))
137                    if mod.removed_perms:
138                        change.append("{0} Removed permissions".format(len(mod.removed_perms)))
139
140                    print("      * {0} ({1})".format(name, ", ".join(change)))
141                    for p in sorted(mod.added_perms):
142                        print("          + {0}".format(p))
143                    for p in sorted(mod.removed_perms):
144                        print("          - {0}".format(p))
145            print()
146
147    if all_differences or args.class_:
148        if diff.added_classes or diff.removed_classes or diff.modified_classes or args.class_:
149            print("Classes ({0} Added, {1} Removed, {2} Modified)".format(
150                len(diff.added_classes), len(diff.removed_classes), len(diff.modified_classes)))
151            if diff.added_classes and not args.stats:
152                print("   Added Classes: {0}".format(len(diff.added_classes)))
153                for c in sorted(diff.added_classes):
154                    print("      + {0}".format(c))
155            if diff.removed_classes and not args.stats:
156                print("   Removed Classes: {0}".format(len(diff.removed_classes)))
157                for c in sorted(diff.removed_classes):
158                    print("      - {0}".format(c))
159            if diff.modified_classes and not args.stats:
160                print("   Modified Classes: {0}".format(len(diff.modified_classes)))
161                for name, mod in sorted(diff.modified_classes.items()):
162                    change = []
163                    if mod.added_perms:
164                        change.append("{0} Added permissions".format(len(mod.added_perms)))
165                    if mod.removed_perms:
166                        change.append("{0} Removed permissions".format(len(mod.removed_perms)))
167
168                    print("      * {0} ({1})".format(name, ", ".join(change)))
169                    for p in sorted(mod.added_perms):
170                        print("          + {0}".format(p))
171                    for p in sorted(mod.removed_perms):
172                        print("          - {0}".format(p))
173            print()
174
175    if all_differences or args.bool_:
176        if diff.added_booleans or diff.removed_booleans or \
177                diff.modified_booleans or args.bool_:
178            print("Booleans ({0} Added, {1} Removed, {2} Modified)".format(
179                len(diff.added_booleans), len(diff.removed_booleans),
180                len(diff.modified_booleans)))
181            if diff.added_booleans and not args.stats:
182                print("   Added Booleans: {0}".format(len(diff.added_booleans)))
183                for a in sorted(diff.added_booleans):
184                    print("      + {0}".format(a))
185            if diff.removed_booleans and not args.stats:
186                print("   Removed Booleans: {0}".format(len(diff.removed_booleans)))
187                for a in sorted(diff.removed_booleans):
188                    print("      - {0}".format(a))
189            if diff.modified_booleans and not args.stats:
190                print("   Modified Booleans: {0}".format(len(diff.modified_booleans)))
191                for name, mod in sorted(diff.modified_booleans.items()):
192                    print("      * {0} (Modified default state)".format(name))
193                    print("          + {0}".format(mod.added_state))
194                    print("          - {0}".format(mod.removed_state))
195
196            print()
197
198    if all_differences or args.role:
199        if diff.added_roles or diff.removed_roles or diff.modified_roles or args.role:
200            print("Roles ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_roles),
201                                                                        len(diff.removed_roles),
202                                                                        len(diff.modified_roles)))
203            if diff.added_roles and not args.stats:
204                print("   Added Roles: {0}".format(len(diff.added_roles)))
205                for r in sorted(diff.added_roles):
206                    print("      + {0}".format(r))
207            if diff.removed_roles and not args.stats:
208                print("   Removed Roles: {0}".format(len(diff.removed_roles)))
209                for r in sorted(diff.removed_roles):
210                    print("      - {0}".format(r))
211            if diff.modified_roles and not args.stats:
212                print("   Modified Roles: {0}".format(len(diff.modified_roles)))
213                for name, mod in sorted(diff.modified_roles.items()):
214                    change = []
215                    if mod.added_types:
216                        change.append("{0} Added types".format(len(mod.added_types)))
217                    if mod.removed_types:
218                        change.append("{0} Removed types".format(len(mod.removed_types)))
219
220                    print("      * {0} ({1})".format(name, ", ".join(change)))
221                    for t in sorted(mod.added_types):
222                        print("          + {0}".format(t))
223                    for t in sorted(mod.removed_types):
224                        print("          - {0}".format(t))
225            print()
226
227    if all_differences or args.type_:
228        if diff.added_types or diff.removed_types or diff.modified_types or args.type_:
229            print("Types ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_types),
230                                                                        len(diff.removed_types),
231                                                                        len(diff.modified_types)))
232            if diff.added_types and not args.stats:
233                print("   Added Types: {0}".format(len(diff.added_types)))
234                for r in sorted(diff.added_types):
235                    print("      + {0}".format(r))
236            if diff.removed_types and not args.stats:
237                print("   Removed Types: {0}".format(len(diff.removed_types)))
238                for r in sorted(diff.removed_types):
239                    print("      - {0}".format(r))
240            if diff.modified_types and not args.stats:
241                print("   Modified Types: {0}".format(len(diff.modified_types)))
242                for name, mod in sorted(diff.modified_types.items()):
243                    change = []
244                    if mod.added_attributes:
245                        change.append("{0} Added attributes".format(len(mod.added_attributes)))
246                    if mod.removed_attributes:
247                        change.append("{0} Removed attributes".format(len(mod.removed_attributes)))
248                    if mod.added_aliases:
249                        change.append("{0} Added aliases".format(len(mod.added_aliases)))
250                    if mod.removed_aliases:
251                        change.append("{0} Removed aliases".format(len(mod.removed_aliases)))
252                    if mod.modified_permissive:
253                        if mod.permissive:
254                            change.append("Removed permissive")
255                        else:
256                            change.append("Added permissive")
257
258                    print("      * {0} ({1})".format(name, ", ".join(change)))
259                    if mod.added_attributes or mod.removed_attributes:
260                        print("          Attributes:")
261                    for t in sorted(mod.added_attributes):
262                        print("          + {0}".format(t))
263                    for t in sorted(mod.removed_attributes):
264                        print("          - {0}".format(t))
265
266                    if mod.added_aliases or mod.removed_aliases:
267                        print("          Aliases:")
268                    for t in sorted(mod.added_aliases):
269                        print("          + {0}".format(t))
270                    for t in sorted(mod.removed_aliases):
271                        print("          - {0}".format(t))
272
273            print()
274
275    if all_differences or args.attribute:
276        if diff.added_type_attributes or diff.removed_type_attributes or \
277                diff.modified_type_attributes or args.attribute:
278            print("Type Attributes ({0} Added, {1} Removed, {2} Modified)".format(
279                len(diff.added_type_attributes), len(diff.removed_type_attributes),
280                len(diff.modified_type_attributes)))
281            if diff.added_type_attributes and not args.stats:
282                print("   Added Type Attributes: {0}".format(len(diff.added_type_attributes)))
283                for a in sorted(diff.added_type_attributes):
284                    print("      + {0}".format(a))
285            if diff.removed_type_attributes and not args.stats:
286                print("   Removed Type Attributes: {0}".format(len(diff.removed_type_attributes)))
287                for a in sorted(diff.removed_type_attributes):
288                    print("      - {0}".format(a))
289            if diff.modified_type_attributes and not args.stats:
290                print("   Modified Type Attributes: {0}".format(len(diff.modified_type_attributes)))
291                for name, mod in sorted(diff.modified_type_attributes.items()):
292                    change = []
293                    if mod.added_types:
294                        change.append("{0} Added types".format(len(mod.added_types)))
295                    if mod.removed_types:
296                        change.append("{0} Removed types".format(len(mod.removed_types)))
297
298                    print("      * {0} ({1})".format(name, ", ".join(change)))
299                    for t in sorted(mod.added_types):
300                        print("          + {0}".format(t))
301                    for t in sorted(mod.removed_types):
302                        print("          - {0}".format(t))
303            print()
304
305    if all_differences or args.user:
306        if diff.added_users or diff.removed_users or diff.modified_users or args.user:
307            print("Users ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_users),
308                                                                        len(diff.removed_users),
309                                                                        len(diff.modified_users)))
310            if diff.added_users and not args.stats:
311                print("   Added Users: {0}".format(len(diff.added_users)))
312                for u in sorted(diff.added_users):
313                    print("      + {0}".format(u))
314            if diff.removed_users and not args.stats:
315                print("   Removed Users: {0}".format(len(diff.removed_users)))
316                for u in sorted(diff.removed_users):
317                    print("      - {0}".format(u))
318            if diff.modified_users and not args.stats:
319                print("   Modified Users: {0}".format(len(diff.modified_users)))
320                for name, mod in sorted(diff.modified_users.items()):
321                    change = []
322                    if mod.added_roles:
323                        change.append("{0} Added roles".format(len(mod.added_roles)))
324                    if mod.removed_roles:
325                        change.append("{0} Removed roles".format(len(mod.removed_roles)))
326                    if mod.removed_level:
327                        change.append("Modified default level")
328                    if mod.removed_range:
329                        change.append("Modified range")
330
331                    print("      * {0} ({1})".format(name, ", ".join(change)))
332                    if mod.added_roles or mod.removed_roles:
333                        print("          Roles:")
334                    for t in sorted(mod.added_roles):
335                        print("          + {0}".format(t))
336                    for t in sorted(mod.removed_roles):
337                        print("          - {0}".format(t))
338
339                    if mod.removed_level:
340                        print("          Default level:")
341                        print("          + {0}".format(mod.added_level))
342                        print("          - {0}".format(mod.removed_level))
343
344                    if mod.removed_range:
345                        print("          Range:")
346                        print("          + {0}".format(mod.added_range))
347                        print("          - {0}".format(mod.removed_range))
348            print()
349
350    if all_differences or args.category:
351        if diff.added_categories or diff.removed_categories or diff.modified_categories \
352                or args.category:
353            print("Categories ({0} Added, {1} Removed, {2} Modified)".format(
354                len(diff.added_categories), len(diff.removed_categories),
355                len(diff.modified_categories)))
356            if diff.added_categories and not args.stats:
357                print("   Added Categories: {0}".format(len(diff.added_categories)))
358                for c in sorted(diff.added_categories):
359                    print("      + {0}".format(c))
360            if diff.removed_categories and not args.stats:
361                print("   Removed Categories: {0}".format(len(diff.removed_categories)))
362                for c in sorted(diff.removed_categories):
363                    print("      - {0}".format(c))
364            if diff.modified_categories and not args.stats:
365                print("   Modified Categories: {0}".format(len(diff.modified_categories)))
366                for name, mod in sorted(diff.modified_categories.items()):
367                    change = []
368                    if mod.added_aliases:
369                        change.append("{0} Added Aliases".format(len(mod.added_aliases)))
370                    if mod.removed_aliases:
371                        change.append("{0} Removed Aliases".format(len(mod.removed_aliases)))
372
373                    print("      * {0} ({1})".format(name, ", ".join(change)))
374                    print("          Aliases:")
375                    for a in sorted(mod.added_aliases):
376                        print("          + {0}".format(a))
377                    for a in sorted(mod.removed_aliases):
378                        print("          - {0}".format(a))
379
380            print()
381
382    if all_differences or args.sensitivity:
383        if diff.added_sensitivities or diff.removed_sensitivities or diff.modified_sensitivities \
384                or args.sensitivity:
385            print("Sensitivities ({0} Added, {1} Removed, {2} Modified)".format(
386                len(diff.added_sensitivities), len(diff.removed_sensitivities),
387                len(diff.modified_sensitivities)))
388            if diff.added_sensitivities and not args.stats:
389                print("   Added Sensitivites: {0}".format(len(diff.added_sensitivities)))
390                for s in sorted(diff.added_sensitivities):
391                    print("      + {0}".format(s))
392            if diff.removed_sensitivities and not args.stats:
393                print("   Removed Sensitivities: {0}".format(len(diff.removed_sensitivities)))
394                for s in sorted(diff.removed_sensitivities):
395                    print("      - {0}".format(s))
396            if diff.modified_sensitivities and not args.stats:
397                print("   Modified Sensitivities: {0}".format(len(diff.modified_sensitivities)))
398                for name, mod in sorted(diff.modified_sensitivities.items()):
399                    change = []
400                    if mod.added_aliases:
401                        change.append("{0} Added Aliases".format(len(mod.added_aliases)))
402                    if mod.removed_aliases:
403                        change.append("{0} Removed Aliases".format(len(mod.removed_aliases)))
404
405                    print("      * {0} ({1})".format(name, ", ".join(change)))
406                    print("          Aliases:")
407                    for a in sorted(mod.added_aliases):
408                        print("          + {0}".format(a))
409                    for a in sorted(mod.removed_aliases):
410                        print("          - {0}".format(a))
411
412            print()
413
414    if all_differences or args.level:
415        if diff.added_levels or diff.removed_levels or \
416                diff.modified_levels or args.level:
417            print("Levels ({0} Added, {1} Removed, {2} Modified)".format(
418                len(diff.added_levels), len(diff.removed_levels),
419                len(diff.modified_levels)))
420            if diff.added_levels and not args.stats:
421                print("   Added Levels: {0}".format(len(diff.added_levels)))
422                for l in sorted(diff.added_levels):
423                    print("      + {0}".format(l))
424            if diff.removed_levels and not args.stats:
425                print("   Removed Levels: {0}".format(len(diff.removed_levels)))
426                for l in sorted(diff.removed_levels):
427                    print("      - {0}".format(l))
428            if diff.modified_levels and not args.stats:
429                print("   Modified Levels: {0}".format(len(diff.modified_levels)))
430                for level, added_categories, removed_categories, _ in sorted(diff.modified_levels,
431                                                                             key=lambda x: x.level):
432                    change = []
433                    if added_categories:
434                        change.append("{0} Added Categories".format(len(added_categories)))
435                    if removed_categories:
436                        change.append("{0} Removed Categories".format(len(removed_categories)))
437
438                    print("      * {0} ({1})".format(level.sensitivity, ", ".join(change)))
439                    for c in sorted(added_categories):
440                        print("          + {0}".format(c))
441                    for c in sorted(removed_categories):
442                        print("          - {0}".format(c))
443            print()
444
445    if all_differences or args.allow:
446        if diff.added_allows or diff.removed_allows or diff.modified_allows or args.allow:
447            print("Allow Rules ({0} Added, {1} Removed, {2} Modified)".format(
448                len(diff.added_allows), len(diff.removed_allows), len(diff.modified_allows)))
449
450            if diff.added_allows and not args.stats:
451                print("   Added Allow Rules: {0}".format(len(diff.added_allows)))
452                for r in sorted(diff.added_allows):
453                    print("      + {0}".format(r))
454
455            if diff.removed_allows and not args.stats:
456                print("   Removed Allow Rules: {0}".format(len(diff.removed_allows)))
457                for r in sorted(diff.removed_allows):
458                    print("      - {0}".format(r))
459
460            if diff.modified_allows and not args.stats:
461                print("   Modified Allow Rules: {0}".format(len(diff.modified_allows)))
462
463                for rule, added_perms, removed_perms, matched_perms in sorted(diff.modified_allows,
464                                                                              key=lambda x: x.rule):
465                    perms = " ".join(chain((p for p in matched_perms),
466                                           ("+" + p for p in added_perms),
467                                           ("-" + p for p in removed_perms)))
468                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
469                        rule, perms)
470
471                    try:
472                        rule_string += " [ {0} ]".format(rule.conditional)
473                    except AttributeError:
474                        pass
475                    print("      * {0}".format(rule_string))
476
477            print()
478
479    if all_differences or args.neverallow:
480        if diff.added_neverallows or diff.removed_neverallows or diff.modified_neverallows or \
481                args.neverallow:
482            print("Neverallow Rules ({0} Added, {1} Removed, {2} Modified)".format(
483                len(diff.added_neverallows), len(diff.removed_neverallows),
484                len(diff.modified_neverallows)))
485
486            if diff.added_neverallows and not args.stats:
487                print("   Added Neverallow Rules: {0}".format(len(diff.added_neverallows)))
488                for r in sorted(diff.added_neverallows):
489                    print("      + {0}".format(r))
490
491            if diff.removed_neverallows and not args.stats:
492                print("   Removed Neverallow Rules: {0}".format(len(diff.removed_neverallows)))
493                for r in sorted(diff.removed_neverallows):
494                    print("      - {0}".format(r))
495
496            if diff.modified_neverallows and not args.stats:
497                print("   Modified Neverallow Rules: {0}".format(len(diff.modified_neverallows)))
498
499                for rule, added_perms, removed_perms, matched_perms in sorted(
500                        diff.modified_neverallows, key=lambda x: x.rule):
501                    perms = " ".join(chain((p for p in matched_perms),
502                                           ("+" + p for p in added_perms),
503                                           ("-" + p for p in removed_perms)))
504                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
505                        rule, perms)
506
507                    try:
508                        rule_string += " [ {0} ]".format(rule.conditional)
509                    except AttributeError:
510                        pass
511                    print("      * {0}".format(rule_string))
512
513            print()
514
515    if all_differences or args.auditallow:
516        if diff.added_auditallows or diff.removed_auditallows or diff.modified_auditallows or \
517                args.auditallow:
518            print("Auditallow Rules ({0} Added, {1} Removed, {2} Modified)".format(
519                len(diff.added_auditallows), len(diff.removed_auditallows),
520                len(diff.modified_auditallows)))
521
522            if diff.added_auditallows and not args.stats:
523                print("   Added Auditallow Rules: {0}".format(len(diff.added_auditallows)))
524                for r in sorted(diff.added_auditallows):
525                    print("      + {0}".format(r))
526
527            if diff.removed_auditallows and not args.stats:
528                print("   Removed Auditallow Rules: {0}".format(len(diff.removed_auditallows)))
529                for r in sorted(diff.removed_auditallows):
530                    print("      - {0}".format(r))
531
532            if diff.modified_auditallows and not args.stats:
533                print("   Modified Auditallow Rules: {0}".format(len(diff.modified_auditallows)))
534
535                for rule, added_perms, removed_perms, matched_perms in sorted(
536                        diff.modified_auditallows, key=lambda x: x.rule):
537                    perms = " ".join(chain((p for p in matched_perms),
538                                           ("+" + p for p in added_perms),
539                                           ("-" + p for p in removed_perms)))
540                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
541                        rule, perms)
542
543                    try:
544                        rule_string += " [ {0} ]".format(rule.conditional)
545                    except AttributeError:
546                        pass
547                    print("      * {0}".format(rule_string))
548
549            print()
550
551    if all_differences or args.dontaudit:
552        if diff.added_dontaudits or diff.removed_dontaudits or diff.modified_dontaudits or \
553                args.dontaudit:
554            print("Dontaudit Rules ({0} Added, {1} Removed, {2} Modified)".format(
555                len(diff.added_dontaudits), len(diff.removed_dontaudits),
556                len(diff.modified_dontaudits)))
557
558            if diff.added_dontaudits and not args.stats:
559                print("   Added Dontaudit Rules: {0}".format(len(diff.added_dontaudits)))
560                for r in sorted(diff.added_dontaudits):
561                    print("      + {0}".format(r))
562
563            if diff.removed_dontaudits and not args.stats:
564                print("   Removed Dontaudit Rules: {0}".format(len(diff.removed_dontaudits)))
565                for r in sorted(diff.removed_dontaudits):
566                    print("      - {0}".format(r))
567
568            if diff.modified_dontaudits and not args.stats:
569                print("   Modified Dontaudit Rules: {0}".format(len(diff.modified_dontaudits)))
570
571                for rule, added_perms, removed_perms, matched_perms in sorted(
572                        diff.modified_dontaudits, key=lambda x: x.rule):
573                    perms = " ".join(chain((p for p in matched_perms),
574                                           ("+" + p for p in added_perms),
575                                           ("-" + p for p in removed_perms)))
576                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
577                        rule, perms)
578
579                    try:
580                        rule_string += " [ {0} ]".format(rule.conditional)
581                    except AttributeError:
582                        pass
583                    print("      * {0}".format(rule_string))
584
585            print()
586
587    if all_differences or args.type_trans:
588        if diff.added_type_transitions or diff.removed_type_transitions or \
589                diff.modified_type_transitions or args.type_trans:
590            print("Type_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
591                len(diff.added_type_transitions), len(diff.removed_type_transitions),
592                len(diff.modified_type_transitions)))
593
594            if diff.added_type_transitions and not args.stats:
595                print("   Added Type_transition Rules: {0}".format(
596                    len(diff.added_type_transitions)))
597                for r in sorted(diff.added_type_transitions):
598                    print("      + {0}".format(r))
599
600            if diff.removed_type_transitions and not args.stats:
601                print("   Removed Type_transition Rules: {0}".format(
602                    len(diff.removed_type_transitions)))
603                for r in sorted(diff.removed_type_transitions):
604                    print("      - {0}".format(r))
605
606            if diff.modified_type_transitions and not args.stats:
607                print("   Modified Type_transition Rules: {0}".format(
608                    len(diff.modified_type_transitions)))
609
610                for rule, added_default, removed_default in sorted(diff.modified_type_transitions,
611                                                                   key=lambda x: x.rule):
612                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
613                        rule, added_default, removed_default)
614
615                    try:
616                        rule_string += " {0}".format(rule.filename)
617                    except AttributeError:
618                        pass
619
620                    rule_string += ";"
621
622                    try:
623                        rule_string += " [ {0} ]".format(rule.conditional)
624                    except AttributeError:
625                        pass
626
627                    print("      * {0}".format(rule_string))
628
629            print()
630
631    if all_differences or args.type_change:
632        if diff.added_type_changes or diff.removed_type_changes or \
633                diff.modified_type_changes or args.type_change:
634            print("Type_change Rules ({0} Added, {1} Removed, {2} Modified)".format(
635                len(diff.added_type_changes), len(diff.removed_type_changes),
636                len(diff.modified_type_changes)))
637
638            if diff.added_type_changes and not args.stats:
639                print("   Added Type_change Rules: {0}".format(len(diff.added_type_changes)))
640                for r in sorted(diff.added_type_changes):
641                    print("      + {0}".format(r))
642
643            if diff.removed_type_changes and not args.stats:
644                print("   Removed Type_change Rules: {0}".format(len(diff.removed_type_changes)))
645                for r in sorted(diff.removed_type_changes):
646                    print("      - {0}".format(r))
647
648            if diff.modified_type_changes and not args.stats:
649                print("   Modified Type_change Rules: {0}".format(len(diff.modified_type_changes)))
650
651                for rule, added_default, removed_default in sorted(diff.modified_type_changes,
652                                                                   key=lambda x: x.rule):
653                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
654                        rule, added_default, removed_default)
655
656                    try:
657                        rule_string += " {0}".format(rule.filename)
658                    except AttributeError:
659                        pass
660
661                    rule_string += ";"
662
663                    try:
664                        rule_string += " [ {0} ]".format(rule.conditional)
665                    except AttributeError:
666                        pass
667
668                    print("      * {0}".format(rule_string))
669
670            print()
671
672    if all_differences or args.type_member:
673        if diff.added_type_members or diff.removed_type_members or \
674                diff.modified_type_members or args.type_member:
675            print("Type_member Rules ({0} Added, {1} Removed, {2} Modified)".format(
676                len(diff.added_type_members), len(diff.removed_type_members),
677                len(diff.modified_type_members)))
678
679            if diff.added_type_members and not args.stats:
680                print("   Added Type_member Rules: {0}".format(len(diff.added_type_members)))
681                for r in sorted(diff.added_type_members):
682                    print("      + {0}".format(r))
683
684            if diff.removed_type_members and not args.stats:
685                print("   Removed Type_member Rules: {0}".format(len(diff.removed_type_members)))
686                for r in sorted(diff.removed_type_members):
687                    print("      - {0}".format(r))
688
689            if diff.modified_type_members and not args.stats:
690                print("   Modified Type_member Rules: {0}".format(len(diff.modified_type_members)))
691
692                for rule, added_default, removed_default in sorted(diff.modified_type_members,
693                                                                   key=lambda x: x.rule):
694                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
695                        rule, added_default, removed_default)
696
697                    try:
698                        rule_string += " {0}".format(rule.filename)
699                    except AttributeError:
700                        pass
701
702                    rule_string += ";"
703
704                    try:
705                        rule_string += " [ {0} ]".format(rule.conditional)
706                    except AttributeError:
707                        pass
708
709                    print("      * {0}".format(rule_string))
710
711            print()
712
713    if all_differences or args.role_allow:
714        if diff.added_role_allows or diff.removed_role_allows or args.role_allow:
715            print("Role allow Rules ({0} Added, {1} Removed)".format(
716                len(diff.added_role_allows), len(diff.removed_role_allows)))
717
718            if diff.added_role_allows and not args.stats:
719                print("   Added Role allow Rules: {0}".format(
720                    len(diff.added_role_allows)))
721                for r in sorted(diff.added_role_allows):
722                    print("      + {0}".format(r))
723
724            if diff.removed_role_allows and not args.stats:
725                print("   Removed Role allow Rules: {0}".format(
726                    len(diff.removed_role_allows)))
727                for r in sorted(diff.removed_role_allows):
728                    print("      - {0}".format(r))
729
730            print()
731
732    if all_differences or args.role_trans:
733        if diff.added_role_transitions or diff.removed_role_transitions or \
734                diff.modified_role_transitions or args.role_trans:
735            print("Role_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
736                len(diff.added_role_transitions), len(diff.removed_role_transitions),
737                len(diff.modified_role_transitions)))
738
739            if diff.added_role_transitions and not args.stats:
740                print("   Added Role_transition Rules: {0}".format(
741                    len(diff.added_role_transitions)))
742                for r in sorted(diff.added_role_transitions):
743                    print("      + {0}".format(r))
744
745            if diff.removed_role_transitions and not args.stats:
746                print("   Removed Role_transition Rules: {0}".format(
747                    len(diff.removed_role_transitions)))
748                for r in sorted(diff.removed_role_transitions):
749                    print("      - {0}".format(r))
750
751            if diff.modified_role_transitions and not args.stats:
752                print("   Modified Role_transition Rules: {0}".format(
753                    len(diff.modified_role_transitions)))
754
755                for rule, added_default, removed_default in sorted(diff.modified_role_transitions,
756                                                                   key=lambda x: x.rule):
757                    rule_string = \
758                        "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
759                            rule, added_default, removed_default)
760
761                    print("      * {0}".format(rule_string))
762
763            print()
764
765    if all_differences or args.range_trans:
766        if diff.added_range_transitions or diff.removed_range_transitions or \
767                diff.modified_range_transitions or args.range_trans:
768            print("Range_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
769                len(diff.added_range_transitions), len(diff.removed_range_transitions),
770                len(diff.modified_range_transitions)))
771
772            if diff.added_range_transitions and not args.stats:
773                print("   Added Range_transition Rules: {0}".format(
774                    len(diff.added_range_transitions)))
775                for r in sorted(diff.added_range_transitions):
776                    print("      + {0}".format(r))
777
778            if diff.removed_range_transitions and not args.stats:
779                print("   Removed Range_transition Rules: {0}".format(
780                    len(diff.removed_range_transitions)))
781                for r in sorted(diff.removed_range_transitions):
782                    print("      - {0}".format(r))
783
784            if diff.modified_range_transitions and not args.stats:
785                print("   Modified Range_transition Rules: {0}".format(
786                    len(diff.modified_range_transitions)))
787
788                for rule, added_default, removed_default in sorted(diff.modified_range_transitions,
789                                                                   key=lambda x: x.rule):
790                    # added brackets around range change for clarity since ranges
791                    # can have '-' and spaces.
792                    rule_string = \
793                        "{0.ruletype} {0.source} {0.target}:{0.tclass} +[{1}] -[{2}]".format(
794                            rule, added_default, removed_default)
795
796                    print("      * {0}".format(rule_string))
797
798            print()
799
800    if all_differences or args.initialsid:
801        if diff.added_initialsids or diff.removed_initialsids or diff.modified_initialsids \
802                or args.initialsid:
803            print("Initial SIDs ({0} Added, {1} Removed, {2} Modified)".format(
804                len(diff.added_initialsids), len(diff.removed_initialsids),
805                len(diff.modified_initialsids)))
806            if diff.added_initialsids and not args.stats:
807                print("   Added Initial SIDs: {0}".format(len(diff.added_initialsids)))
808                for s in sorted(diff.added_initialsids):
809                    print("      + {0}".format(s.statement()))
810            if diff.removed_initialsids and not args.stats:
811                print("   Removed Initial SIDs: {0}".format(len(diff.removed_initialsids)))
812                for s in sorted(diff.removed_initialsids):
813                    print("      - {0}".format(s.statement()))
814            if diff.modified_initialsids and not args.stats:
815                print("   Modified Initial SIDs: {0}".format(len(diff.modified_initialsids)))
816                for name, mod in sorted(diff.modified_initialsids.items()):
817                    print("      * {0} +[{1.added_context}] -[{1.removed_context}];".format(
818                          name, mod))
819
820            print()
821
822    if all_differences or args.fs_use:
823        if diff.added_fs_uses or diff.removed_fs_uses or diff.modified_fs_uses \
824                or args.fs_use:
825            print("Fs_use ({0} Added, {1} Removed, {2} Modified)".format(
826                len(diff.added_fs_uses), len(diff.removed_fs_uses),
827                len(diff.modified_fs_uses)))
828            if diff.added_fs_uses and not args.stats:
829                print("   Added Fs_use: {0}".format(len(diff.added_fs_uses)))
830                for s in sorted(diff.added_fs_uses):
831                    print("      + {0}".format(s))
832            if diff.removed_fs_uses and not args.stats:
833                print("   Removed Fs_use: {0}".format(len(diff.removed_fs_uses)))
834                for s in sorted(diff.removed_fs_uses):
835                    print("      - {0}".format(s))
836            if diff.modified_fs_uses and not args.stats:
837                print("   Modified Fs_use: {0}".format(len(diff.modified_fs_uses)))
838                for entry in sorted(diff.modified_fs_uses, key=lambda x: x.rule):
839                    print("      * {0.ruletype} {0.fs} +[{1}] -[{2}];".format(
840                          entry.rule, entry.added_context, entry.removed_context))
841
842            print()
843
844    if all_differences or args.genfscon:
845        if diff.added_genfscons or diff.removed_genfscons or diff.modified_genfscons \
846                or args.genfscon:
847            print("Genfscons ({0} Added, {1} Removed, {2} Modified)".format(
848                len(diff.added_genfscons), len(diff.removed_genfscons),
849                len(diff.modified_genfscons)))
850            if diff.added_genfscons and not args.stats:
851                print("   Added Genfscons: {0}".format(len(diff.added_genfscons)))
852                for s in sorted(diff.added_genfscons):
853                    print("      + {0}".format(s))
854            if diff.removed_genfscons and not args.stats:
855                print("   Removed Genfscons: {0}".format(len(diff.removed_genfscons)))
856                for s in sorted(diff.removed_genfscons):
857                    print("      - {0}".format(s))
858            if diff.modified_genfscons and not args.stats:
859                print("   Modified Genfscons: {0}".format(len(diff.modified_genfscons)))
860                for entry in sorted(diff.modified_genfscons, key=lambda x: x.rule):
861                    print("      * genfscon {0.fs} {0.path} {0.filetype} +[{1}] -[{2}];".format(
862                          entry.rule, entry.added_context, entry.removed_context))
863
864            print()
865
866    if all_differences or args.netifcon:
867        if diff.added_netifcons or diff.removed_netifcons or \
868                diff.modified_netifcons or args.netifcon:
869            print("Netifcons ({0} Added, {1} Removed, {2} Modified)".format(
870                len(diff.added_netifcons), len(diff.removed_netifcons),
871                len(diff.modified_netifcons)))
872            if diff.added_netifcons and not args.stats:
873                print("   Added Netifcons: {0}".format(len(diff.added_netifcons)))
874                for n in sorted(diff.added_netifcons):
875                    print("      + {0}".format(n))
876            if diff.removed_netifcons and not args.stats:
877                print("   Removed Netifcons: {0}".format(len(diff.removed_netifcons)))
878                for n in sorted(diff.removed_netifcons):
879                    print("      - {0}".format(n))
880            if diff.modified_netifcons and not args.stats:
881                print("   Modified Netifcons: {0}".format(len(diff.modified_netifcons)))
882                for entry in sorted(diff.modified_netifcons, key=lambda x: x.rule):
883                    # This output is different than other statements because
884                    # it becomes difficult to read if this was condensed
885                    # into a single line, especially if both contexts
886                    # are modified.
887                    change = []
888                    if entry.removed_context:
889                        change.append("Modified Context")
890                    if entry.removed_packet:
891                        change.append("Modified Packet Context")
892
893                    print("      * {0.netif} ({1})".format(entry.rule, ", ".join(change)))
894
895                    if entry.removed_context:
896                        print("          Context:")
897                        print("             + {0}".format(entry.added_context))
898                        print("             - {0}".format(entry.removed_context))
899                    if entry.removed_packet:
900                        print("          Packet Context:")
901                        print("             + {0}".format(entry.added_packet))
902                        print("             - {0}".format(entry.removed_packet))
903
904            print()
905
906    if all_differences or args.nodecon:
907        if diff.added_nodecons or diff.removed_nodecons or diff.modified_nodecons \
908                or args.nodecon:
909            print("Nodecons ({0} Added, {1} Removed, {2} Modified)".format(
910                len(diff.added_nodecons), len(diff.removed_nodecons),
911                len(diff.modified_nodecons)))
912            if diff.added_nodecons and not args.stats:
913                print("   Added Nodecons: {0}".format(len(diff.added_nodecons)))
914                for n in sorted(diff.added_nodecons):
915                    print("      + {0}".format(n))
916            if diff.removed_nodecons and not args.stats:
917                print("   Removed Nodecons: {0}".format(len(diff.removed_nodecons)))
918                for n in sorted(diff.removed_nodecons):
919                    print("      - {0}".format(n))
920            if diff.modified_nodecons and not args.stats:
921                print("   Modified Nodecons: {0}".format(len(diff.modified_nodecons)))
922                for con, added_context, removed_context in sorted(diff.modified_nodecons,
923                                                                  key=lambda x: x.rule):
924                    print("      * nodecon {0.address} {0.netmask} +[{1}] -[{2}];".format(
925                          con, added_context, removed_context))
926
927            print()
928
929    if all_differences or args.portcon:
930        if diff.added_portcons or diff.removed_portcons or diff.modified_portcons \
931                or args.portcon:
932            print("Portcons ({0} Added, {1} Removed, {2} Modified)".format(
933                len(diff.added_portcons), len(diff.removed_portcons),
934                len(diff.modified_portcons)))
935            if diff.added_portcons and not args.stats:
936                print("   Added Portcons: {0}".format(len(diff.added_portcons)))
937                for n in sorted(diff.added_portcons):
938                    print("      + {0}".format(n))
939            if diff.removed_portcons and not args.stats:
940                print("   Removed Portcons: {0}".format(len(diff.removed_portcons)))
941                for n in sorted(diff.removed_portcons):
942                    print("      - {0}".format(n))
943            if diff.modified_portcons and not args.stats:
944                print("   Modified Portcons: {0}".format(len(diff.modified_portcons)))
945                for con, added_context, removed_context in sorted(diff.modified_portcons,
946                                                                  key=lambda x: x.rule):
947                    low, high = con.ports
948                    if low == high:
949                        print("      * portcon {0.protocol} {1} +[{2}] -[{3}];".format(
950                              con, low, added_context, removed_context))
951                    else:
952                        print("      * portcon {0.protocol} {1}-{2} +[{3}] -[{4}];".format(
953                              con, low, high, added_context, removed_context))
954
955            print()
956
957    if all_differences or args.polcap:
958        if diff.added_polcaps or diff.removed_polcaps or args.polcap:
959            print("Policy Capabilities ({0} Added, {1} Removed)".format(
960                len(diff.added_polcaps), len(diff.removed_polcaps)))
961            if diff.added_polcaps and not args.stats:
962                print("   Added Policy Capabilities: {0}".format(len(diff.added_polcaps)))
963                for n in sorted(diff.added_polcaps):
964                    print("      + {0}".format(n))
965            if diff.removed_polcaps and not args.stats:
966                print("   Removed Policy Capabilities: {0}".format(len(diff.removed_polcaps)))
967                for n in sorted(diff.removed_polcaps):
968                    print("      - {0}".format(n))
969
970            print()
971
972    if all_differences or args.default:
973        if diff.added_defaults or diff.removed_defaults or args.default:
974            print("Defaults ({0} Added, {1} Removed, {2} Modified)".format(
975                len(diff.added_defaults), len(diff.removed_defaults), len(diff.modified_defaults)))
976            if diff.added_defaults and not args.stats:
977                print("   Added Defaults: {0}".format(len(diff.added_defaults)))
978                for d in sorted(diff.added_defaults):
979                    print("      + {0}".format(d))
980            if diff.removed_defaults and not args.stats:
981                print("   Removed Defaults: {0}".format(len(diff.removed_defaults)))
982                for d in sorted(diff.removed_defaults):
983                    print("      - {0}".format(d))
984            if diff.modified_defaults and not args.stats:
985                print("   Modified Defaults: {0}".format(len(diff.modified_defaults)))
986                for default, added_default, removed_default, added_range, removed_range in sorted(
987                        diff.modified_defaults, key=lambda x: x.rule):
988                    line = "      * {0.ruletype} {0.tclass} ".format(default)
989                    if removed_default:
990                        line += "+{0} -{1}".format(added_default, removed_default)
991                    else:
992                        line += default.default
993
994                    if removed_range:
995                        line += " +{0} -{1};".format(added_range, removed_range)
996                    else:
997                        try:
998                            line += " {0};".format(default.default_range)
999                        except AttributeError:
1000                            line += ";"
1001                    print(line)
1002
1003            print()
1004
1005except Exception as err:
1006    if args.debug:
1007        import traceback
1008        traceback.print_exc()
1009    else:
1010        print(err)
1011
1012    sys.exit(1)
1013