Files
openldap-log-parser/openldap-log-parser.go

222 lines
7.6 KiB
Go

package openldaplog
import (
"fmt"
"time"
"errors"
"regexp"
"strconv"
)
const (
SyslogPri = `(?:<\d{1,3}>)?`
TimeFormat = "Jan 2 15:04:05"
TimeFormatISO8601 = "2006-01-02T15:04:05.999999-07:00"
// group[1]
TimeRE = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|Z))`
IPv4RE = `(?:\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`
// FIXME: Not very strict
IPv6RE = `(?:(?:[0-9a-fA-F]{1,4}\:){7})[0-9a-fA-F]{1,4}`
HostRE = `([0-9A-Za-z\-\_\.]*)`
ProcessRE = `(slapd\[[0-9]{1,7}\])(?:\:)?`
// group[4]
ConnIdRE = `conn=([0-9]{4,10})`
ConnFdRE = `(?:fd=([0-9]{1,10}))?`
OperationIdRE = `(?:op=([0-9]{1,10}))?`
AcceptRE = `(?:ACCEPT from IP=(` + IPv4RE + `)\:([0-9]{1,5}) \(IP=(` + IPv4RE + `)\:([0-9]{1,5})\))?`
// group[11]
STARTTLSRE = `(STARTTLS$)?`
// group[12], group[13]
BindMethodRE = `(?:BIND dn="(.*)" method=([0-9]{1,3}))?`
BindMechRE = `(?:BIND dn=".*" mech=([a-zA-Z]*) (?:bind_ssf=([0-9]{1,3}) )?ssf=([0-9]{1,3}))?`
// group[17], group[18], group[19], group[20], group[21], group[22], group[23]
ResultRE = `(?:(RESULT) (?:tag=([0-9]{1,3})|oid=([0-9\.]*)) err=([0-9]{1,3}) (?:qtime=([0-9\.]{1,10}) )?(?:etime=([0-9\.]{1,10}) )?text=(.*))?`
SearchBaseRE = `(?:SRCH base="(.*)" scope=([0-2]) deref=([0-3]) filter="(.*)")?`
// group[28]
SearchAttrRE = `(?:SRCH attr=(.*))?`
// group[29], group[30], group[31], group[32], group[33], group[34], group[35]
SearchResultRE = `(?:(SEARCH RESULT) tag=([0-9]{1,3}) err=([0-9]{1,3}) (?:qtime=([0-9\.]{1,10}) )?(?:etime=([0-9\.]{1,10}) )?nentries=([0-9]{1,9}) text=(.*))?`
ModDnRE = `(?:MOD dn="(.*)")?`
ModAttrRE = `(?:MOD attr=(.*))?`
// group[38]
PassModRE = `(?:PASSMOD id="(.*)" new)?`
UnbindRE = `(UNBIND)?`
// group[40]
ConnClosedRE = `(closed)?(?: \(connection lost\))?`
// group[41]
AddDnRE = `(?:ADD dn="(.*)")?`
// group[42]
DelDnRE = `(?:DEL dn="(.*)")?`
LogLineRE = SyslogPri + TimeRE + ` ` + HostRE + ` ` + ProcessRE + ` ` + ConnIdRE + ` ` + ConnFdRE + OperationIdRE + ` ` +
AcceptRE + STARTTLSRE + BindMethodRE + BindMechRE + ResultRE + SearchBaseRE + SearchAttrRE + SearchResultRE + ModDnRE + ModAttrRE +
PassModRE + UnbindRE + ConnClosedRE + AddDnRE + DelDnRE
)
type (
OpenldapLog struct {
LogFormat LogFormat
Regexp *regexp.Regexp
}
LogFormat struct {
Time *time.Time `json:"time"`
Hostname string `json:"hostname"`
Process string `json:"process"`
ClientIp string `json:"client_ip"`
ClientPort int `json:"client_port"`
ServerIp string `json:"server_ip"`
ServerPort int `json:"server_port"`
BindDN string `json:"bind_dn"`
ConnId int `json:"conn_id"`
ConnFd int `json:"conn_fd"`
OpId int `json:"op_id"`
OpType string `json:"op_type"`
BindMethod string `json:"bind_method"`
BindMech string `json:"bind_mech"`
BindSSF string `json:"bind_ssf"`
SSF string `json:"ssf"`
ModDN string `json:"mod_dn"`
ModAttr string `json:"mod_attr"`
AddDN string `json:"add_dn"`
DelDN string `json:"del_dn"`
PassModDN string `json:"passmod_dn"`
Result bool
ResTag string `json:"result_tag"`
ResOid string `json:"result_oid"`
ResErr int `json:"result_err"`
ResQTime string `json:"result_qtime"`
ResETime string `json:"result_etime"`
ResText string `json:"result_text"`
SearchResult bool
SearchBase string `json:"search_base"`
SearchScope string `json:"search_scope"`
SearchDeref string `json:"search_deref"`
SearchFilter string `json:"search_filter"`
SearchAttr string `json:"search_attr"`
SearchResTag string `json:"search_res_tag"`
SearchResErr int `json:"search_res_err"`
SearchResQTime string `json:"search_res_qtime"`
SearchResETime string `json:"search_res_etime"`
SearchResNEntries int `json:"search_res_nentries"`
SearchResText string `json:"search_res_text"`
}
)
var (
gDebug bool
)
func NewOpenldapLog(debug bool) *OpenldapLog {
if debug {
gDebug = true
fmt.Printf("DEBUG: Regexp will display on next line\n")
fmt.Printf("%s\n", LogLineRE)
}
return &OpenldapLog{
Regexp: regexp.MustCompile(LogLineRE),
}
}
func (o *OpenldapLog) Parse(text []byte) (LogFormat, error) {
re := o.Regexp.Copy()
group := re.FindSubmatch(text)
if len(group) == 0 {
err := errors.New("Error: Line do not match regex")
return LogFormat{}, err
}
var t time.Time
t, err := time.ParseInLocation(TimeFormat, string(group[1]), time.Local)
if err != nil {
t, err = time.ParseInLocation(TimeFormatISO8601, string(group[1]), time.Local)
if err != nil {
return LogFormat{}, err
}
}
cid,_ := strconv.Atoi(string(group[4]))
cfd,_ := strconv.Atoi(string(group[5]))
opid,_ := strconv.Atoi(string(group[6]))
cport,_ := strconv.Atoi(string(group[8]))
sport,_ := strconv.Atoi(string(group[10]))
result := false
if string(group[17]) == "RESULT" {
result = true
}
rerr,_ := strconv.Atoi(string(group[20]))
sresult := false
if string(group[29]) == "SEARCH RESULT" {
sresult = true
}
serr,_ := strconv.Atoi(string(group[31]))
srentries, _ := strconv.Atoi(string(group[34]))
logFormat := LogFormat{
Time: &t,
Hostname: string(group[2]),
Process: string(group[3]),
ConnId: cid,
ConnFd: cfd,
OpId: opid,
ClientIp: string(group[7]),
ClientPort: cport,
ServerIp: string(group[9]),
ServerPort: sport,
// "STARTTLS"
BindDN: string(group[12]),
BindMethod: string(group[13]),
BindMech: string(group[14]),
BindSSF: string(group[15]),
SSF: string(group[16]),
Result: result,
ResTag: string(group[18]),
ResOid: string(group[19]),
ResErr: rerr,
ResQTime: string(group[21]),
ResETime: string(group[22]),
ResText: string(group[23]),
SearchBase: string(group[24]),
SearchScope: string(group[25]),
SearchDeref: string(group[26]),
SearchFilter: string(group[27]),
SearchAttr: string(group[28]),
SearchResult: sresult,
SearchResTag: string(group[30]),
SearchResErr: serr,
SearchResQTime: string(group[32]),
SearchResETime: string(group[33]),
SearchResNEntries: srentries,
SearchResText: string(group[35]),
ModDN: string(group[36]),
ModAttr: string(group[37]),
PassModDN: string(group[38]),
AddDN: string(group[41]),
DelDN: string(group[42]),
}
// Now handle Operation Type
if len(group[12]) > 0 || len(group[13]) > 0 || len(group[14]) > 0 || len(group[15]) > 0 || len(group[16]) > 0 {
logFormat.OpType = "bind"
} else if len(group[11]) > 0 {
logFormat.OpType = "starttls"
} else if len(group[24]) > 0 {
logFormat.OpType = "search"
} else if len(group[7]) > 0 {
logFormat.OpType = "accept"
} else if len(group[36]) > 0 || len(group[37]) > 0 {
logFormat.OpType = "mod"
} else if len(group[38]) > 0 {
logFormat.OpType = "passmod"
} else if len(group[39]) > 0 {
logFormat.OpType = "unbind"
} else if len(group[40]) > 0 {
logFormat.OpType = "close"
} else if len(group[41]) > 0 {
logFormat.OpType = "add"
} else if len(group[42]) > 0 {
logFormat.OpType = "del"
}
return logFormat, nil
}