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 }