-
Notifications
You must be signed in to change notification settings - Fork 67
/
seinfoflow
executable file
·177 lines (151 loc) · 6.64 KB
/
seinfoflow
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
#!/usr/bin/env python3
# Copyright 2014-2015, Tresys Technology, LLC
#
# SPDX-License-Identifier: GPL-2.0-only
#
import argparse
import sys
import logging
import signal
import warnings
from typing import Dict, Optional
import networkx as nx
import setools
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
parser = argparse.ArgumentParser(
description="SELinux policy information flow analysis tool.",
epilog="If no analysis is selected, all information flow out of the source will be printed.")
parser.add_argument("--full", help="Print full rule lists for information flows.",
action="store_true")
parser.add_argument("--version", action="version", version=setools.__version__)
parser.add_argument("--stats", action="store_true",
help="Display statistics at the end of the analysis.")
parser.add_argument("-v", "--verbose", action="store_true",
help="Print extra informational messages")
parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
settings = parser.add_argument_group("Analysis settings")
settings.add_argument("-p", "--policy",
help="Path to SELinux policy to analyze.")
settings.add_argument("-m", "--map",
help="Path to alternative permission map file.")
settings.add_argument("-s", "--source", required=True,
help="Source type of the analysis.")
settings.add_argument("-t", "--target", default="",
help="Target type of the analysis.")
alg = parser.add_argument_group("Analysis algorithm")
alg.add_argument("-S", "--shortest_path", action="store_true",
help="Calculate all shortest paths.")
alg.add_argument("-A", "--all_paths", type=int, metavar="MAX_STEPS",
help="Calculate all paths, with the specified maximum path length. (Expensive)")
opts = parser.add_argument_group("Analysis options")
opts.add_argument("-r", "--reverse", action="store_true",
help="Display information flows into the source type. "
"No effect if a target type is specified.")
opts.add_argument("-w", "--min_weight", default=3, type=int,
help="Minimum permission weight. Default is 3.")
opts.add_argument("-l", "--limit_flows", default=0, type=int,
help="Limit to the specified number of flows. Default is unlimited.")
opts.add_argument("-b", "--booleans", default=None,
help="Specify the boolean values to use."
" Options are default, or \"foo:true,bar:false...\"")
opts.add_argument("-o", "--output_file", help="Output file for graphical results, PNG format.")
opts.add_argument("exclude", nargs="*",
help="List of excluded types in the analysis.")
args = parser.parse_args()
if not args.target and (args.shortest_path or args.all_paths):
parser.error("The target type must be specified to determine a path.")
if args.target and not (args.shortest_path or args.all_paths):
parser.error("A target type is not used for flows in/out of a type.")
if args.limit_flows < 0:
parser.error("Limit on information flows cannot be negative.")
if args.debug:
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
if not sys.warnoptions:
warnings.simplefilter("default")
elif args.verbose:
logging.basicConfig(level=logging.INFO, format='%(message)s')
if not sys.warnoptions:
warnings.simplefilter("default")
else:
logging.basicConfig(level=logging.WARNING, format='%(message)s')
if not sys.warnoptions:
warnings.simplefilter("ignore")
booleans: Optional[Dict[str, bool]] = None
if args.booleans == 'default':
booleans = {}
elif args.booleans is not None:
booleans = {}
for boolean in args.booleans.split(','):
try:
key, value = boolean.split(':')
if value.lower() == 'true':
booleans[key] = True
elif value.lower() == 'false':
booleans[key] = False
else:
parser.error("Conditional value must be true or false.")
except ValueError:
parser.error("Expected boolean format foo:true,bar:false")
try:
p = setools.SELinuxPolicy(args.policy)
m = setools.PermissionMap(args.map)
g = setools.InfoFlowAnalysis(p, m, min_weight=args.min_weight, exclude=args.exclude,
booleans=booleans)
flownum: int = 0
flow: setools.InfoFlowPath
stepnum: int = 0
step: setools.InfoFlowStep
if args.shortest_path or args.all_paths:
g.source = args.source
g.target = args.target
if args.shortest_path:
g.mode = setools.InfoFlowAnalysis.Mode.ShortestPaths
else:
g.mode = setools.InfoFlowAnalysis.Mode.AllPaths
g.depth_limit = args.all_paths
if args.output_file:
pgv = nx.nx_agraph.to_agraph(g.graphical_results())
pgv.draw(path=args.output_file, prog="dot", format="png")
else:
for flownum, flow in enumerate(g.results(), start=1): # type: ignore
print(f"Flow {flownum}:")
for stepnum, step in enumerate(flow, start=1):
if args.full:
print(f" Step {stepnum}: {step:full}\n")
else:
print(f" Step {stepnum}: {step}")
if args.limit_flows and flownum >= args.limit_flows:
break
print(f"\n{flownum} information flow(s) found.")
else: # single step, direct info flow
if args.reverse:
g.mode = setools.InfoFlowAnalysis.Mode.FlowsIn
g.target = args.source
else:
g.mode = setools.InfoFlowAnalysis.Mode.FlowsOut
g.source = args.source
if args.output_file:
pgv = nx.nx_agraph.to_agraph(g.graphical_results())
pgv.draw(path=args.output_file, prog="dot", format="png")
else:
for flownum, step in enumerate(g.results(), start=1): # type: ignore
if args.full:
print(f"Flow {flownum}: {step:full}\n")
else:
print(f"Flow {flownum}: {step}")
if args.limit_flows and flownum >= args.limit_flows:
break
print(f"\n{flownum} information flow(s) found.")
if args.stats:
print("\nGraph statistics:")
print(g.get_stats())
except AssertionError:
# Always provide a traceback for assertion errors
raise
except Exception as err:
if args.debug:
raise
else:
print(err)
sys.exit(1)