|
10 | 10 | */ |
11 | 11 |
|
12 | 12 | import python |
13 | | -import semmle.python.ApiGraphs |
| 13 | +import FluentApiModel |
14 | 14 |
|
| 15 | +// string foo(ProtocolRestriction r) { result = r.getRestriction() } |
15 | 16 | // The idea is to track flow from the creation of an insecure context to a use |
16 | 17 | // such as `wrap_socket`. There should be a data-flow path for each insecure version |
17 | 18 | // and each path should have a version specific sanitizer. This will allow fluent api |
18 | 19 | // style code to block the paths one by one. |
19 | | -// |
20 | | -// class InsecureContextCreation extends DataFlow::CfgNode { |
21 | | -// override CallNode node; |
22 | | -// InsecureContextCreation() { |
23 | | -// this = API::moduleImport("ssl").getMember("SSLContext").getACall() and |
24 | | -// insecure_version().asCfgNode() in [node.getArg(0), node.getArgByName("protocol")] |
25 | | -// } |
26 | | -// } |
27 | | -// class InsecureSSLContextCreation extends DataFlow::CfgNode { |
28 | | -// override CallNode node; |
29 | | -// InsecureSSLContextCreation() { |
30 | | -// this = API::moduleImport("ssl").getMember("create_default_context").getACall() |
31 | | -// or |
32 | | -// this = API::moduleImport("ssl").getMember("SSLContext").getACall() and |
33 | | -// API::moduleImport("ssl").getMember("PROTOCOL_TLS").getAUse().asCfgNode() in [ |
34 | | -// node.getArg(0), node.getArgByName("protocol") |
35 | | -// ] |
36 | | -// } |
37 | | -// } |
38 | | -abstract class ContextCreation extends DataFlow::CfgNode { |
39 | | - abstract DataFlow::CfgNode getProtocol(); |
40 | | -} |
41 | | - |
42 | | -abstract class ConnectionCreation extends DataFlow::CfgNode { |
43 | | - abstract DataFlow::CfgNode getContext(); |
44 | | -} |
45 | | - |
46 | | -class ProtocolRestriction extends DataFlow::CfgNode { |
47 | | - abstract DataFlow::CfgNode getContext(); |
48 | | - |
49 | | - abstract string getRestriction(); |
50 | | -} |
51 | | - |
52 | | -abstract class TlsLibrary extends string { |
53 | | - TlsLibrary() { this in ["ssl", "pyOpenSSL"] } |
54 | | - |
55 | | - abstract string specific_insecure_version_name(); |
56 | | - |
57 | | - abstract string unspecific_version_name(); |
58 | | - |
59 | | - abstract API::Node version_constants(); |
60 | | - |
61 | | - DataFlow::Node insecure_version() { |
62 | | - result = version_constants().getMember(specific_insecure_version_name()).getAUse() |
63 | | - } |
64 | | - |
65 | | - DataFlow::Node unspecific_version() { |
66 | | - result = version_constants().getMember(unspecific_version_name()).getAUse() |
67 | | - } |
68 | | - |
69 | | - abstract DataFlow::CfgNode default_context_creation(); |
70 | | - |
71 | | - abstract ContextCreation specific_context_creation(); |
72 | | - |
73 | | - ContextCreation insecure_context_creation() { |
74 | | - result = specific_context_creation() and |
75 | | - result.getProtocol() = insecure_version() |
76 | | - } |
77 | | - |
78 | | - DataFlow::CfgNode unspecific_context_creation() { |
79 | | - result = default_context_creation() |
80 | | - or |
81 | | - result = specific_context_creation() and |
82 | | - result.(ContextCreation).getProtocol() = unspecific_version() |
83 | | - } |
84 | | - |
85 | | - /** A connection is created in an outright insecure manner. */ |
86 | | - abstract DataFlow::CfgNode insecure_connection_creation(); |
87 | | - |
88 | | - /** A connection is created from a context. */ |
89 | | - abstract ConnectionCreation connection_creation(); |
90 | | - |
91 | | - abstract ProtocolRestriction protocol_restriction(); |
92 | | -} |
93 | | - |
94 | | -module ssl { |
95 | | - class SSLContextCreation extends ContextCreation { |
96 | | - override CallNode node; |
97 | | - |
98 | | - SSLContextCreation() { this = API::moduleImport("ssl").getMember("SSLContext").getACall() } |
99 | | - |
100 | | - override DataFlow::CfgNode getProtocol() { |
101 | | - result.getNode() in [node.getArg(0), node.getArgByName("protocol")] |
102 | | - } |
103 | | - } |
104 | | - |
105 | | - class WrapSocketCall extends ConnectionCreation { |
106 | | - override CallNode node; |
107 | | - |
108 | | - WrapSocketCall() { node.getFunction().(AttrNode).getName() = "wrap_socket" } |
109 | | - |
110 | | - override DataFlow::CfgNode getContext() { |
111 | | - result.getNode() = node.getFunction().(AttrNode).getObject() |
112 | | - } |
113 | | - } |
114 | | - |
115 | | - class OptionsAugOr extends ProtocolRestriction { |
116 | | - string restriction; |
117 | | - |
118 | | - OptionsAugOr() { |
119 | | - exists(AugAssign aa, AttrNode attr | |
120 | | - aa.getOperation().getOp() instanceof BitOr and |
121 | | - aa.getTarget() = attr.getNode() and |
122 | | - attr.getName() = "options" and |
123 | | - attr.getObject() = node and |
124 | | - aa.getValue() = API::moduleImport("ssl").getMember(restriction).getAUse().asExpr() |
125 | | - ) |
126 | | - } |
127 | | - |
128 | | - override DataFlow::CfgNode getContext() { result = this } |
129 | | - |
130 | | - override string getRestriction() { result = restriction } |
131 | | - } |
132 | | - |
133 | | - class Ssl extends TlsLibrary { |
134 | | - Ssl() { this = "ssl" } |
135 | | - |
136 | | - override string specific_insecure_version_name() { |
137 | | - result in [ |
138 | | - "PROTOCOL_SSLv2", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_TLSv1", |
139 | | - "PROTOCOL_TLSv1_1" |
140 | | - ] |
141 | | - } |
142 | | - |
143 | | - override string unspecific_version_name() { result = "PROTOCOL_TLS" } |
144 | | - |
145 | | - override API::Node version_constants() { result = API::moduleImport("ssl") } |
146 | | - |
147 | | - override DataFlow::CfgNode default_context_creation() { |
148 | | - result = API::moduleImport("ssl").getMember("create_default_context").getACall() |
149 | | - } |
150 | | - |
151 | | - override ContextCreation specific_context_creation() { result instanceof SSLContextCreation } |
152 | | - |
153 | | - override DataFlow::CfgNode insecure_connection_creation() { |
154 | | - result = API::moduleImport("ssl").getMember("wrap_socket").getACall() |
155 | | - } |
156 | | - |
157 | | - override ConnectionCreation connection_creation() { result instanceof WrapSocketCall } |
158 | | - |
159 | | - override ProtocolRestriction protocol_restriction() { result instanceof OptionsAugOr } |
160 | | - } |
161 | | -} |
162 | | - |
163 | | -module pyOpenSSL { |
164 | | - class PyOpenSSLContextCreation extends ContextCreation { |
165 | | - override CallNode node; |
166 | | - |
167 | | - PyOpenSSLContextCreation() { |
168 | | - this = API::moduleImport("pyOpenSSL").getMember("SSL").getMember("Context").getACall() |
169 | | - } |
170 | | - |
171 | | - override DataFlow::CfgNode getProtocol() { |
172 | | - result.getNode() in [node.getArg(0), node.getArgByName("method")] |
173 | | - } |
174 | | - } |
175 | | - |
176 | | - class ConnectionCall extends ConnectionCreation { |
177 | | - override CallNode node; |
178 | | - |
179 | | - ConnectionCall() { |
180 | | - this = API::moduleImport("pyOpenSSL").getMember("SSL").getMember("Connection").getACall() |
181 | | - } |
182 | | - |
183 | | - override DataFlow::CfgNode getContext() { |
184 | | - result.getNode() in [node.getArg(0), node.getArgByName("context")] |
185 | | - } |
186 | | - } |
187 | | - |
188 | | - class SetOptionsCall extends ProtocolRestriction { |
189 | | - override CallNode node; |
190 | | - |
191 | | - SetOptionsCall() { node.getFunction().(AttrNode).getName() = "set_options" } |
192 | | - |
193 | | - override DataFlow::CfgNode getContext() { |
194 | | - result.getNode() = node.getFunction().(AttrNode).getObject() |
195 | | - } |
196 | | - |
197 | | - override string getRestriction() { |
198 | | - API::moduleImport("PyOpenSSL").getMember("SSL").getMember(result).getAUse().asCfgNode() in [ |
199 | | - node.getArg(0), node.getArgByName("options") |
200 | | - ] |
201 | | - } |
202 | | - } |
203 | | - |
204 | | - class PyOpenSSL extends TlsLibrary { |
205 | | - PyOpenSSL() { this = "pyOpenSSL" } |
206 | | - |
207 | | - override string specific_insecure_version_name() { |
208 | | - result in ["SSLv2_METHOD", "SSLv23_METHOD", "SSLv3_METHOD", "TLSv1_METHOD", "TLSv1_1_METHOD"] |
209 | | - } |
210 | | - |
211 | | - override string unspecific_version_name() { result = "TLS_METHOD" } |
212 | | - |
213 | | - override API::Node version_constants() { |
214 | | - result = API::moduleImport("pyOpenSSL").getMember("SSL") |
215 | | - } |
216 | | - |
217 | | - override DataFlow::CfgNode default_context_creation() { none() } |
218 | | - |
219 | | - override ContextCreation specific_context_creation() { |
220 | | - result instanceof PyOpenSSLContextCreation |
221 | | - } |
222 | | - |
223 | | - override DataFlow::CfgNode insecure_connection_creation() { none() } |
224 | | - |
225 | | - override ConnectionCreation connection_creation() { result instanceof ConnectionCall } |
226 | | - |
227 | | - override ProtocolRestriction protocol_restriction() { result instanceof SetOptionsCall } |
228 | | - } |
229 | | -} |
230 | | - |
231 | | -class InsecureContextConfiguration extends DataFlow::Configuration { |
232 | | - TlsLibrary library; |
233 | | - |
234 | | - InsecureContextConfiguration() { this = library + ["AllowsTLSv1", "AllowsTLSv1_1"] } |
235 | | - |
236 | | - override predicate isSource(DataFlow::Node source) { |
237 | | - source = library.unspecific_context_creation() |
238 | | - } |
239 | | - |
240 | | - override predicate isSink(DataFlow::Node sink) { |
241 | | - sink = library.connection_creation().getContext() |
242 | | - } |
243 | | - |
244 | | - abstract string flag(); |
245 | | - |
246 | | - override predicate isBarrierOut(DataFlow::Node node) { |
247 | | - exists(ProtocolRestriction r | |
248 | | - r = library.protocol_restriction() and |
249 | | - node = r.getContext() and |
250 | | - r.getRestriction() = flag() |
251 | | - ) |
252 | | - } |
253 | | -} |
254 | | - |
255 | | -class AllowsTLSv1 extends InsecureContextConfiguration { |
256 | | - AllowsTLSv1() { this = library + "AllowsTLSv1" } |
257 | | - |
258 | | - override string flag() { result = "OP_NO_TLSv1" } |
259 | | -} |
260 | | - |
261 | | -class AllowsTLSv1_1 extends InsecureContextConfiguration { |
262 | | - AllowsTLSv1_1() { this = library + "AllowsTLSv1_1" } |
263 | | - |
264 | | - override string flag() { result = "OP_NO_TLSv1_1" } |
265 | | -} |
266 | | - |
267 | | -predicate unsafe_connection_creation(DataFlow::Node node, string insecure_version) { |
268 | | - exists(AllowsTLSv1 c | c.hasFlowTo(node)) and |
269 | | - insecure_version = "TLSv1" |
270 | | - or |
271 | | - exists(AllowsTLSv1_1 c | c.hasFlowTo(node)) and |
272 | | - insecure_version = "TLSv1" |
273 | | - or |
274 | | - exists(TlsLibrary l | l.insecure_connection_creation() = node) and |
275 | | - insecure_version = "[multiple]" |
276 | | -} |
277 | | - |
278 | | -predicate unsafe_context_creation(DataFlow::Node node, string insecure_version) { |
279 | | - exists(TlsLibrary l, ContextCreation cc | cc = l.insecure_context_creation() | |
280 | | - cc = node and insecure_version = cc.getProtocol().toString() |
281 | | - ) |
282 | | -} |
283 | | - |
284 | 20 | from DataFlow::Node node, string insecure_version |
285 | 21 | where |
286 | 22 | unsafe_connection_creation(node, insecure_version) |
|
0 commit comments