222 lines
7.6 KiB
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
|
|
}
|