|
| 1 | +import go |
| 2 | +import DataFlow::PathGraph |
| 3 | +import semmle.go.dataflow.barrierguardutil.RegexpCheck |
| 4 | + |
| 5 | +module ImproperLdapAuth { |
| 6 | + /** |
| 7 | + * A LDAP connection node. |
| 8 | + */ |
| 9 | + abstract class LdapConn extends DataFlow::CallNode { } |
| 10 | + |
| 11 | + /** |
| 12 | + * A sink that is vulnerable to improper LDAP Authentication vulnerabilities. |
| 13 | + */ |
| 14 | + abstract class LdapAuthSink extends DataFlow::Node { } |
| 15 | + |
| 16 | + /** |
| 17 | + * A sanitizer function that prevents improper LDAP Authentication attacks. |
| 18 | + */ |
| 19 | + abstract class LdapSanitizer extends DataFlow::Node { } |
| 20 | + |
| 21 | + /** |
| 22 | + * A vulnerable argument to `go-ldap` or `ldap`'s `bind` function (Only v2). |
| 23 | + */ |
| 24 | + private class GoLdapBindSink extends LdapAuthSink { |
| 25 | + GoLdapBindSink() { |
| 26 | + exists(Method meth | |
| 27 | + meth.hasQualifiedName("gopkg.in/ldap.v2", "Conn", "Bind") and |
| 28 | + this = meth.getACall().getArgument(1) |
| 29 | + ) |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + /** |
| 34 | + * A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs. |
| 35 | + * |
| 36 | + * This is overapproximate: we do not attempt to reason about the correctness of the regexp. |
| 37 | + */ |
| 38 | + class RegexpCheckAsBarrierGuard extends RegexpCheckBarrier, LdapSanitizer { } |
| 39 | + |
| 40 | + /** |
| 41 | + * An empty string. |
| 42 | + */ |
| 43 | + class EmptyString extends DataFlow::Node { |
| 44 | + EmptyString() { this.asExpr().getStringValue() = "" } |
| 45 | + } |
| 46 | + |
| 47 | + private predicate equalityAsSanitizerGuard(DataFlow::Node g, Expr e, boolean outcome) { |
| 48 | + exists(DataFlow::Node nonConstNode, DataFlow::Node constNode, DataFlow::EqualityTestNode eq | |
| 49 | + g = eq and |
| 50 | + nonConstNode = eq.getAnOperand() and |
| 51 | + not nonConstNode.isConst() and |
| 52 | + constNode = eq.getAnOperand() and |
| 53 | + constNode.isConst() and |
| 54 | + e = nonConstNode.asExpr() and |
| 55 | + ( |
| 56 | + // If `constNode` is not an empty string a comparison is considered a sanitizer |
| 57 | + not constNode instanceof EmptyString and outcome = eq.getPolarity() |
| 58 | + or |
| 59 | + // If `constNode` is an empty string a not comparison is considered a sanitizer |
| 60 | + constNode instanceof EmptyString and outcome = eq.getPolarity().booleanNot() |
| 61 | + ) |
| 62 | + ) |
| 63 | + } |
| 64 | + |
| 65 | + /** |
| 66 | + * An equality check comparing a data-flow node against a constant string, considered as |
| 67 | + * a barrier guard for sanitizing untrusted user input. |
| 68 | + */ |
| 69 | + class EqualityAsSanitizerGuard extends LdapSanitizer { |
| 70 | + EqualityAsSanitizerGuard() { |
| 71 | + this = DataFlow::BarrierGuard<equalityAsSanitizerGuard/3>::getABarrierNode() |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + private module Config implements DataFlow::ConfigSig { |
| 76 | + predicate isSource(DataFlow::Node source) { |
| 77 | + source instanceof UntrustedFlowSource or source instanceof EmptyString |
| 78 | + } |
| 79 | + |
| 80 | + predicate isSink(DataFlow::Node sink) { sink instanceof LdapAuthSink } |
| 81 | + |
| 82 | + predicate isBarrier(DataFlow::Node node) { node instanceof LdapSanitizer } |
| 83 | + } |
| 84 | + |
| 85 | + /** |
| 86 | + * Tracks taint flow for reasoning about improper ldap auth vulnerabilities |
| 87 | + * with sinks which are not sanitized by string comparisons. |
| 88 | + */ |
| 89 | + module Flow = TaintTracking::Global<Config>; |
| 90 | +} |
0 commit comments