summaryrefslogtreecommitdiffstats
path: root/ldap/admin/src/scripts/failedbinds.py
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/admin/src/scripts/failedbinds.py')
-rw-r--r--ldap/admin/src/scripts/failedbinds.py169
1 files changed, 169 insertions, 0 deletions
diff --git a/ldap/admin/src/scripts/failedbinds.py b/ldap/admin/src/scripts/failedbinds.py
new file mode 100644
index 00000000..8afe0ff1
--- /dev/null
+++ b/ldap/admin/src/scripts/failedbinds.py
@@ -0,0 +1,169 @@
+import re
+import os, os.path
+
+# regex that matches a BIND request line
+regex_num = r'[-]?\d+' # matches numbers including negative
+regex_new_conn = re.compile(r'^(\[.+\]) (conn=%s) (fd=%s) (slot=%s) (?:SSL )?connection from (\S+)' % (regex_num, regex_num, regex_num))
+regex_sslinfo = re.compile(r'^\[.+\] (conn=%s) SSL (.+)$' % regex_num)
+regex_bind_req = re.compile(r'^(\[.+\]) (conn=%s) (op=%s) BIND dn=(.+) method=(\S+) version=\d ?(?:mech=(\S+))?' % (regex_num, regex_num))
+regex_bind_res = re.compile(r'^(\[.+\]) (conn=%s) (op=%s) RESULT err=(%s) tag=97 ' % (regex_num, regex_num, regex_num))
+regex_unbind = re.compile(r'^\[.+\] (conn=%s) op=%s UNBIND' % (regex_num, regex_num))
+regex_closed = re.compile(r'^(\[.+\]) (conn=%s) (op=%s) fd=%s closed' % (regex_num, regex_num, regex_num))
+regex_ssl_map_fail = re.compile(r'^\[.+\] (conn=%s) (SSL failed to map client certificate.*)$' % regex_num)
+
+# bind errors we can ignore
+ignore_errors = {'0': 'Success',
+ '10': 'Referral',
+ '14': 'SASL Bind In Progress'
+ }
+
+REQ = 0
+RES = 1
+
+class Conn:
+ def __init__(self, timestamp, conn, fd, slot, ip):
+ self.conn = conn
+ self.fd = fd
+ self.slot = slot
+ self.ip = ip
+ self.timestamp = timestamp
+ self.ops = {}
+ self.sslinfo = ''
+
+ def addssl(self, sslinfo):
+ if self.sslinfo and sslinfo:
+ self.sslinfo += ' '
+ self.sslinfo += sslinfo
+
+ def addreq(self, timestamp, opnum, dn, method, mech='SIMPLE'):
+ retval = None
+ if opnum in self.ops: # result came before request?
+ op = self.ops.pop(opnum) # grab the op and remove from list
+ if op[RES]['errnum'] in ignore_errors: # don't care about this op
+ return retval
+ if not mech: mech = "SIMPLE"
+ op[REQ] = {'dn': dn, 'method': method, 'timestamp': timestamp,
+ 'mech': mech}
+ retval = self.logstr(opnum, op)
+ else: # store request until we get the result
+ op = [None, None] # new empty list
+ if not mech: mech = "SIMPLE"
+ op[REQ] = {'dn': dn, 'method': method, 'timestamp': timestamp,
+ 'mech': mech}
+ self.ops[opnum] = op
+ return retval
+
+ def addres(self, timestamp, opnum, errnum):
+ retval = None
+ if opnum in self.ops:
+ op = self.ops.pop(opnum) # grab the op and remove from list
+ if errnum in ignore_errors: # don't care about this op
+ return retval
+ op[RES] = {'errnum': errnum, 'timestamp': timestamp}
+ retval = self.logstr(opnum, op)
+ else: # result came before request in access log - store until we find request
+ op = [None, None] # new empty list
+ op[RES] = {'errnum': errnum, 'timestamp': timestamp}
+ self.ops[opnum] = op
+ return retval
+
+ def logstr(self, opnum, op):
+ # timestamp connnum opnum err=X request timestamp dn=Y method=Z mech=W timestamp ip=ip
+ logstr = '%s %s %s err=%s REQUEST %s dn=%s method=%s mech=%s %s ip=%s extra=%s' % (
+ op[RES]['timestamp'], self.conn, opnum, op[RES]['errnum'],
+ op[REQ]['timestamp'], op[REQ]['dn'], op[REQ]['method'], op[REQ]['mech'],
+ self.timestamp, self.ip, self.sslinfo
+ )
+ return logstr
+
+# key is conn=X
+# val is ops hash
+# key is op=Y
+# value is list
+# list[0] is BIND request
+# list[1] is RESULT
+conns = {}
+
+# file to log failed binds to
+logf = None
+
+def pre(plgargs):
+ global logf
+ logfile = plgargs.get('logfile', None)
+ if not logfile:
+ print "Error: missing required argument failedbinds.logfile"
+ return False
+ needchmod = False
+ if not os.path.isfile(logfile): needchmod = True
+ logf = open(logfile, 'a', 0) # 0 for unbuffered output
+ if needchmod: os.chmod(logfile, 0600)
+ return True
+
+def post():
+ global logf
+ logf.close()
+ logf = None
+
+def plugin(line):
+ # is this a new conn line?
+ match = regex_new_conn.match(line)
+ if match:
+ (timestamp, connid, fdid, slotid, ip) = match.groups()
+ if connid in conns: conns.pop(connid) # remove old one, if any
+ conn = Conn(timestamp, connid, fdid, slotid, ip)
+ conns[connid] = conn
+ return True
+
+ # is this an UNBIND line?
+ match = regex_unbind.match(line)
+ if match:
+ connid = match.group(1)
+ if connid in conns: conns.pop(connid) # remove it
+ return True
+
+ # is this a closed line?
+ match = regex_closed.match(line)
+ if match:
+ (timestamp, connid, opid) = match.groups()
+ if connid in conns: conns.pop(connid) # remove it
+ return True
+
+ # is this an SSL info line?
+ match = regex_sslinfo.match(line)
+ if match:
+ (connid, sslinfo) = match.groups()
+ if connid in conns:
+ conns[connid].addssl(sslinfo)
+ return True
+
+ # is this a line with extra SSL mapping info?
+ match = regex_ssl_map_fail.match(line)
+ if match:
+ (connid, sslinfo) = match.groups()
+ if connid in conns:
+ conns[connid].addssl(sslinfo)
+ return True
+
+ # is this a REQUEST line?
+ match = regex_bind_req.match(line)
+ if match:
+ (timestamp, connid, opnum, dn, method, mech) = match.groups()
+ # should have seen new conn line - if not, have to create a dummy one
+ conn = conns.get(connid, Conn('unknown', connid, '', '', 'unknown'))
+ logmsg = conn.addreq(timestamp, opnum, dn, method, mech)
+ if logmsg:
+ logf.write(logmsg + "\n")
+ return True
+
+ # is this a RESULT line?
+ match = regex_bind_res.match(line)
+ if match:
+ (timestamp, connid, opnum, errnum) = match.groups()
+ # should have seen new conn line - if not, have to create a dummy one
+ conn = conns.get(connid, Conn('unknown', connid, '', '', 'unknown'))
+ logmsg = conn.addres(timestamp, opnum, errnum)
+ if logmsg:
+ logf.write(logmsg + "\n")
+ return True
+
+ return True # no match