9 Commits

Author SHA1 Message Date
yo
92ba4e4ca6 Resolve event name 2022-01-06 17:29:16 +01:00
yo
8fd6e20cbd Resolve auid 2022-01-04 18:48:18 +01:00
yo
92feeacc5c version bump 2022-01-04 17:51:24 +01:00
yo
8a93dcfc26 Uid & gid resolution, oneline, noresolve 2022-01-04 17:50:34 +01:00
yo
716a3cd0f8 Add go.mod 2022-01-04 11:06:12 +01:00
yo
0579cfea1d Version bump, fix EOF error, move PrintIPv4FromInt 2022-01-04 11:03:58 +01:00
yo
501b371936 Merge branch 'master' of ssh://git.nosd.in:2222/yo/libbsm 2022-01-04 10:48:29 +01:00
yo
5970632c31 AUT_TEXT support 2022-01-04 10:47:10 +01:00
yo
0c7c123fd9 Ipv4/v6 distinction fix 2022-01-04 10:22:57 +01:00
4 changed files with 600 additions and 142 deletions

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module godit
go 1.17
require github.com/spf13/pflag v1.0.5

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=

622
libbsm.go
View File

@ -1,23 +1,42 @@
// This is an implementation of libbsm // This is an implementation of libbsm
// Copyright johan@nosd.in 2021 // Copyright johan@nosd.in 2021
// //
// +build freebsd
//
// Use libc to get pw name from uid
package main package main
/*
#cgo CFLAGS: -I /usr/lib
#cgo LDFLAGS: -L. -lc
#include <sys/types.h>
#include <stdlib.h>
#include <utmpx.h>
#include <grp.h>
#include <pwd.h>
*/
import "C"
import ( import (
"io" "io"
"os" "os"
"fmt" "fmt"
// "net"
"time" "time"
"bufio"
"bytes" "bytes"
"strings"
"strconv"
"encoding/binary" "encoding/binary"
) )
const ( const (
// bsm/libbsm.h // bsm/libbsm.h
AUDIT_MAX_ARGS = 128 AUDIT_MAX_ARGS = 128
AUDIT_EVENT_FILE = "/etc/security/audit_event"
// sys/bsm/audit.h // sys/bsm/audit.h
MAXAUDITDATA = (0x8000 - 1) MAXAUDITDATA = (0x8000 - 1)
MAX_AUDIT_RECORD_SIZE = MAXAUDITDATA MAX_AUDIT_RECORD_SIZE = MAXAUDITDATA
// Max length for a Path (AUT_PATH) or an arg (AUT_EXEC_ARGS) // Max length for a Path (AUT_PATH) or an arg (AUT_EXEC_ARGS)
@ -95,9 +114,28 @@ const (
// Display control // Display control
PRT_ONELINE = 1 PRT_ONELINE = 1
PRT_NORESOLVE_USER = 2
) )
var (
// A global user/uid cache
gUsers []user
// A global group/gid cache
gGroups []group
// Cache of audit_event file
gEventDB []event
)
type event struct {
Type int
Name string
Desc string
Class string
}
// Fields types, from https://github.com/freebsd/freebsd-src/blob/main/contrib/openbsm/bsm/libbsm.h // Fields types, from https://github.com/freebsd/freebsd-src/blob/main/contrib/openbsm/bsm/libbsm.h
// Abstraction of a record // Abstraction of a record
@ -225,8 +263,9 @@ type Tid32 struct {
type Tid32Ex struct { type Tid32Ex struct {
Port uint32 Port uint32
Ttype uint32 Ttype uint32
IpVers uint32 // 0x10 = IPv6 IpVers uint32 // 0x10 = IPv6, 0x04 = IPv4
Addr [4]uint32 // 4 bytes long if IpVers == 0x10, 1 byte long if IpVers == 4 Addr4 uint32 // 4 bytes long if IpVers == 0x04
Addr6 [4]uint32 // 4x4 bytes long if IpVers == 0x10
} }
type Subject64 struct { type Subject64 struct {
@ -264,8 +303,9 @@ type Tid64 struct {
type Tid64Ex struct { type Tid64Ex struct {
Port uint64 Port uint64
Ttype uint32 Ttype uint32
IpVers uint32 // 0x10 = IPv6 IpVers uint32 // 0x10 = IPv6, 0x04 = IPv4
Addr [4]uint32 Addr4 uint32
Addr6 [4]uint32
} }
type Exit struct { type Exit struct {
@ -273,7 +313,147 @@ type Exit struct {
Ret uint32 Ret uint32
} }
type Text struct {
Length uint16
Text []byte
}
/* Utilities */ /* Utilities */
// users ID for resolution
type user struct {
uid uint32
name string
}
// groups ID for resolution
type group struct {
gid uint32
name string
}
/* Utilities */
// Return uid if user not found
func getUserName(uid uint32) (string, error) {
for _, u := range gUsers {
if u.uid == uid {
return u.name, nil
}
}
// Not found in cache, get it from system query
u, err := getUserNameByUid(uid)
if err != nil {
// If not found, return user object with name = uid
if err.Error() == "User ID not found" {
u.uid = uid
u.name = strconv.FormatUint(uint64(uid), 10)
gUsers = append(gUsers, u)
return u.name, err
} else {
return "", err
}
}
gUsers = append(gUsers, u)
return u.name, nil
}
func getUserNameByUid(uid uint32) (user, error) {
var pw *C.struct_passwd
var usr user
pw = C.getpwuid((C.uint32_t)(uid))
if pw == nil {
return usr, fmt.Errorf("User ID not found")
}
usr.uid = uid
usr.name = C.GoString(pw.pw_name)
return usr, nil
}
func getGroupName(gid uint32) (string, error) {
for _, g := range gGroups {
if g.gid == gid {
return g.name, nil
}
}
// Not found in cache, get it from system query
g, err := getGroupNameByGid(gid)
if err != nil {
// If not found, return group object with name = gid
if err.Error() == "Group ID not found" {
g.gid = gid
g.name = strconv.FormatUint(uint64(gid), 10)
gGroups = append(gGroups, g)
return g.name, err
} else {
return "", err
}
}
gGroups = append(gGroups, g)
return g.name, nil
}
func getGroupNameByGid(gid uint32) (group, error) {
var gr *C.struct_group
var grp group
gr = C.getgrgid((C.uint32_t)(gid))
if gr == nil {
return grp, fmt.Errorf("Group ID not found")
}
grp.gid = gid
grp.name = C.GoString(gr.gr_name)
return grp, nil
}
func getEventName(event uint16) (string,error) {
if len(gEventDB) == 0 {
loadEventDB()
}
for _, ev := range gEventDB {
if ev.Type == int(event) {
return ev.Desc, nil
}
}
return "", fmt.Errorf("Event ID not found: %x\n", event)
}
// We load the entire file in memory
func loadEventDB() error {
file, err := os.Open(AUDIT_EVENT_FILE)
if err != nil {
return err
}
defer file.Close()
fileScan := bufio.NewScanner(file)
fileScan.Split(bufio.ScanLines)
for fileScan.Scan() {
line := fileScan.Text()
if strings.HasPrefix(line, "#") {
continue
}
eventStr := strings.Split(line, ":")
if len(eventStr) != 4 {
continue
}
t, _ := strconv.Atoi(eventStr[0])
gEventDB = append(gEventDB, event{Type: t,
Name: eventStr[1],
Desc: eventStr[2],
Class: eventStr[3],})
}
return nil
}
func PrintIpv4FromInt(ipv4int uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", ipv4int & 0xFF000000 >> 24, ipv4int & 0x00FF0000 >> 16,
ipv4int & 0x0000FF00 >> 8, ipv4int & 0x000000FF)
}
func PrintIpv6FromInt(ipv6int [4]uint32) string { func PrintIpv6FromInt(ipv6int [4]uint32) string {
//return fmt.Sprintf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", //return fmt.Sprintf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
return fmt.Sprintf("%x:%x:%x:%x:%x:%x:%x:%x", return fmt.Sprintf("%x:%x:%x:%x:%x:%x:%x:%x",
@ -283,7 +463,6 @@ func PrintIpv6FromInt(ipv6int [4]uint32) string {
ipv6int[3] & 0xFFFF0000 >> 16, ipv6int[3] & 0x0000FFFF) ipv6int[3] & 0xFFFF0000 >> 16, ipv6int[3] & 0x0000FFFF)
} }
/* Records structs implementation */ /* Records structs implementation */
func NewHeader32(h Header32) *Header32 { func NewHeader32(h Header32) *Header32 {
return &Header32{ return &Header32{
@ -335,10 +514,15 @@ func (h *Header32) LoadFromBinary(file *os.File) error {
*/ */
func (h *Header32) Print(file *os.File, delimiter string, flags int) { func (h *Header32) Print(file *os.File, delimiter string, flags int) {
t := time.Unix((int64)(h.S), 0) t := time.Unix((int64)(h.S), 0)
fmt.Fprintf(file, "header%s%v%s%v%s%v%s%v%s%v%s%v", delimiter, h.Size, delimiter, h.Version, delimiter, // We dont care for error
h.E_type, delimiter, h.E_mod, delimiter, t.Format(time.UnixDate), delimiter, h.Msec) evdesc, _ := getEventName(h.E_type)
fmt.Fprintf(file, "header%s%v%s%v%s%s%s%v%s%v%s%v", delimiter, h.Size, delimiter, h.Version, delimiter,
//h.E_type, delimiter, h.E_mod, delimiter, t.Format(time.UnixDate), delimiter, h.Msec)
evdesc, delimiter, h.E_mod, delimiter, t.Format(time.UnixDate), delimiter, h.Msec)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -380,9 +564,9 @@ func (e *ExecArg) LoadFromBinary(file *os.File) error {
return fmt.Errorf("Error searching for null terminated exec arg: Loop exec n%d, offset of record start: %x, error : %v", i, startOf, err) return fmt.Errorf("Error searching for null terminated exec arg: Loop exec n%d, offset of record start: %x, error : %v", i, startOf, err)
} }
// Allocate before reading // Allocate before reading
//e.Text[i] = make([]byte, len(buf))
totLen += int64(len(arg)) totLen += int64(len(arg))
e.Text = append(e.Text, arg) //e.Text = append(e.Text, arg) // Discard last 0
e.Text = append(e.Text, arg[:len(arg)-1])
} }
startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart) startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart)
@ -404,6 +588,8 @@ func (e *ExecArg) Print(file *os.File, delimiter string, flags int) {
} }
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -443,7 +629,7 @@ func (p *Path) LoadFromBinary(file *os.File) error {
return fmt.Errorf("Error searching for null terminated path: offset of record start: %x, error : %v", startOf, err) return fmt.Errorf("Error searching for null terminated path: offset of record start: %x, error : %v", startOf, err)
} }
totLen := int64(len(arg)) totLen := int64(len(arg))
p.Path = arg p.Path = arg[:totLen-1]
startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart) startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart)
if err != nil { if err != nil {
@ -457,6 +643,8 @@ func (p *Path) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "path%s%s", delimiter, string(p.Path)) fmt.Fprintf(file, "path%s%s", delimiter, string(p.Path))
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -498,11 +686,24 @@ func (a *Attribute32) LoadFromBinary(file *os.File) error {
} }
func (a *Attribute32) Print(file *os.File, delimiter string, flags int) { func (a *Attribute32) Print(file *os.File, delimiter string, flags int) {
var user string
var group string
// TODO : resolve Uid and Gid (also support domain accounts) // TODO : resolve Uid and Gid (also support domain accounts)
fmt.Fprintf(file, "attribute%s%o%s%v%s%v%s%v%s%v%s%v", delimiter, a.Mode, delimiter, a.Uid, delimiter, if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
a.Gid, delimiter, a.Fsid, delimiter, a.Nid, delimiter, a.Dev) user = string(a.Uid)
group = string(a.Gid)
} else {
user, _ = getUserName(a.Uid)
group, _ = getGroupName(a.Gid)
}
fmt.Fprintf(file, "attribute%s%o%s%v%s%v%s%v%s%v%s%v", delimiter, a.Mode, delimiter, user, delimiter,
group, delimiter, a.Fsid, delimiter, a.Nid, delimiter, a.Dev)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -544,11 +745,22 @@ func (a *Attribute64) LoadFromBinary(file *os.File) error {
} }
func (a *Attribute64) Print(file *os.File, delimiter string, flags int) { func (a *Attribute64) Print(file *os.File, delimiter string, flags int) {
var user string
var group string
// TODO : resolve Uid and Gid (also support domain accounts) // TODO : resolve Uid and Gid (also support domain accounts)
fmt.Fprintf(file, "attribute%s%o%s%v%s%v%s%v%s%v%s%v", delimiter, a.Mode, delimiter, a.Uid, delimiter, if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
a.Gid, delimiter, a.Fsid, delimiter, a.Nid, delimiter, a.Dev) user = string(a.Uid)
group = string(a.Gid)
} else {
user, _ = getUserName(a.Uid)
group, _ = getGroupName(a.Gid)
}
fmt.Fprintf(file, "attribute%s%o%s%v%s%v%s%v%s%v%s%v", delimiter, a.Mode, delimiter, user, delimiter,
group, delimiter, a.Fsid, delimiter, a.Nid, delimiter, a.Dev)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -594,17 +806,32 @@ func (s *Subject32) LoadFromBinary(file *os.File) error {
return nil return nil
} }
func PrintIpv4FromInt(ipv4int uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", ipv4int & 0xFF000000 >> 24, ipv4int & 0x00FF0000 >> 16,
ipv4int & 0x0000FF00 >> 8, ipv4int & 0x000000FF)
}
func (s *Subject32) Print(file *os.File, delimiter string, flags int) { func (s *Subject32) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "subject%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, s.Auid, delimiter, s.Euid, delimiter, s.Egid, var auser string
delimiter, s.Ruid, delimiter, s.Rgid, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter, s.Tid.IpVers, var euser string
var egroup string
var ruser string
var rgroup string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(s.Auid)
euser = string(s.Euid)
egroup = string(s.Egid)
ruser = string(s.Ruid)
rgroup = string(s.Rgid)
} else {
auser, _ = getUserName(s.Auid)
euser, _ = getUserName(s.Euid)
egroup, _ = getGroupName(s.Egid)
ruser, _ = getUserName(s.Ruid)
rgroup, _ = getGroupName(s.Rgid)
}
fmt.Fprintf(file, "subject%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser, delimiter, egroup,
delimiter, ruser, delimiter, rgroup, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter, s.Tid.IpVers,
delimiter, PrintIpv4FromInt(s.Tid.Addr)) delimiter, PrintIpv4FromInt(s.Tid.Addr))
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -651,11 +878,31 @@ func (p *Process32) LoadFromBinary(file *os.File) error {
} }
func (p *Process32) Print(file *os.File, delimiter string, flags int) { func (p *Process32) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "process%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, p.Auid, delimiter, p.Euid, delimiter, p.Egid, var auser string
delimiter, p.Ruid, delimiter, p.Rgid, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter, p.Tid.IpVers, var euser string
var egroup string
var ruser string
var rgroup string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(p.Auid)
euser = string(p.Euid)
egroup = string(p.Egid)
ruser = string(p.Ruid)
rgroup = string(p.Rgid)
} else {
auser, _ = getUserName(p.Auid)
euser, _ = getUserName(p.Euid)
egroup, _ = getGroupName(p.Egid)
ruser, _ = getUserName(p.Ruid)
rgroup, _ = getGroupName(p.Rgid)
}
fmt.Fprintf(file, "process%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser, delimiter, egroup,
delimiter, ruser, delimiter, rgroup, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter, p.Tid.IpVers,
delimiter, PrintIpv4FromInt(p.Tid.Addr)) delimiter, PrintIpv4FromInt(p.Tid.Addr))
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -696,18 +943,59 @@ func (s *Subject32Ex) LoadFromBinary(file *os.File) error {
err = binary.Read(file, binary.BigEndian, &s.Sid) err = binary.Read(file, binary.BigEndian, &s.Sid)
if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Sid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Sid from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &s.Tid) err = binary.Read(file, binary.BigEndian, &s.Tid.Port)
if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Tid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Tid.Port from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &s.Tid.Ttype)
if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Tid.Ttype from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &s.Tid.IpVers)
if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Tid.IpVers from file: %v", err) }
if s.Tid.IpVers == 0x10 {
err = binary.Read(file, binary.BigEndian, &s.Tid.Addr6)
if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Tid.Addr6 from file: %v", err) }
} else if s.Tid.IpVers == 0x04 {
err = binary.Read(file, binary.BigEndian, &s.Tid.Addr4)
if err != nil { return fmt.Errorf("Unable to read Subject32Ex.Tid.Addr4 from file: %v", err) }
}
return nil return nil
} }
func (s *Subject32Ex) Print(file *os.File, delimiter string, flags int) { func (s *Subject32Ex) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "subject_ex%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, s.Auid, delimiter, s.Euid, var auser string
delimiter, s.Egid, delimiter, s.Ruid, delimiter, s.Rgid, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter, var euser string
s.Tid.Ttype, delimiter, PrintIpv6FromInt(s.Tid.Addr)) var egroup string
var ruser string
var rgroup string
var ip string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(s.Auid)
euser = string(s.Euid)
egroup = string(s.Egid)
ruser = string(s.Ruid)
rgroup = string(s.Rgid)
} else {
auser, _ = getUserName(s.Auid)
euser, _ = getUserName(s.Euid)
egroup, _ = getGroupName(s.Egid)
ruser, _ = getUserName(s.Ruid)
rgroup, _ = getGroupName(s.Rgid)
}
if s.Tid.IpVers == 0x04 {
ip = PrintIpv4FromInt(s.Tid.Addr4)
} else {
ip = PrintIpv6FromInt(s.Tid.Addr6)
}
fmt.Fprintf(file, "subject_ex%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser,
delimiter, egroup, delimiter, ruser, delimiter, rgroup, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter,
s.Tid.Ttype, delimiter, ip)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -748,18 +1036,61 @@ func (p *Process32Ex) LoadFromBinary(file *os.File) error {
err = binary.Read(file, binary.BigEndian, &p.Sid) err = binary.Read(file, binary.BigEndian, &p.Sid)
if err != nil { return fmt.Errorf("Unable to read Process32Ex.Sid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Process32Ex.Sid from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &p.Tid) err = binary.Read(file, binary.BigEndian, &p.Tid.Port)
if err != nil { return fmt.Errorf("Unable to read Process32Ex.Tid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Process32Ex.Tid.Port from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &p.Tid.Ttype)
if err != nil { return fmt.Errorf("Unable to read Process32Ex.Tid.Ttype from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &p.Tid.IpVers)
if err != nil { return fmt.Errorf("Unable to read Process32Ex.Tid.IpVers from file: %v", err) }
if p.Tid.IpVers == 0x10 {
err = binary.Read(file, binary.BigEndian, &p.Tid.Addr6)
if err != nil { return fmt.Errorf("Unable to read Process32Ex.Tid.Addr6 from file: %v", err) }
} else if p.Tid.IpVers == 0x04 {
err = binary.Read(file, binary.BigEndian, &p.Tid.Addr4)
if err != nil { return fmt.Errorf("Unable to read Process32Ex.Tid.Addr4 from file: %v", err) }
}
return nil return nil
} }
func (p *Process32Ex) Print(file *os.File, delimiter string, flags int) { func (p *Process32Ex) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "process_ex%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, p.Auid, delimiter, p.Euid, var auser string
delimiter, p.Egid, delimiter, p.Ruid, delimiter, p.Rgid, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter, var euser string
p.Tid.Ttype, delimiter, PrintIpv6FromInt(p.Tid.Addr)) var egroup string
var ruser string
var rgroup string
var ip string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(p.Auid)
euser = string(p.Euid)
egroup = string(p.Egid)
ruser = string(p.Ruid)
rgroup = string(p.Rgid)
} else {
auser, _ = getUserName(p.Auid)
euser, _ = getUserName(p.Euid)
egroup, _ = getGroupName(p.Egid)
ruser, _ = getUserName(p.Ruid)
rgroup, _ = getGroupName(p.Rgid)
}
if p.Tid.IpVers == 0x04 {
ip = PrintIpv4FromInt(p.Tid.Addr4)
} else {
ip = PrintIpv6FromInt(p.Tid.Addr6)
}
fmt.Fprintf(file, "process_ex%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser,
delimiter, egroup, delimiter, ruser, delimiter, rgroup, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter,
p.Tid.Ttype, delimiter, ip)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -806,11 +1137,31 @@ func (s *Subject64) LoadFromBinary(file *os.File) error {
} }
func (s *Subject64) Print(file *os.File, delimiter string, flags int) { func (s *Subject64) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "subject%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, s.Auid, delimiter, s.Euid, delimiter, s.Egid, var auser string
delimiter, s.Ruid, delimiter, s.Rgid, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter, s.Tid.IpVers, var euser string
var egroup string
var ruser string
var rgroup string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(s.Auid)
euser = string(s.Euid)
egroup = string(s.Egid)
ruser = string(s.Ruid)
rgroup = string(s.Rgid)
} else {
auser, _ = getUserName(s.Auid)
euser, _ = getUserName(s.Euid)
egroup, _ = getGroupName(s.Egid)
ruser, _ = getUserName(s.Ruid)
rgroup, _ = getGroupName(s.Rgid)
}
fmt.Fprintf(file, "subject%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser, delimiter, egroup,
delimiter, ruser, delimiter, rgroup, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter, s.Tid.IpVers,
delimiter, PrintIpv4FromInt(s.Tid.Addr)) delimiter, PrintIpv4FromInt(s.Tid.Addr))
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -857,11 +1208,31 @@ func (p *Process64) LoadFromBinary(file *os.File) error {
} }
func (p *Process64) Print(file *os.File, delimiter string, flags int) { func (p *Process64) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "process%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, p.Auid, delimiter, p.Euid, delimiter, p.Egid, var auser string
delimiter, p.Ruid, delimiter, p.Rgid, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter, p.Tid.IpVers, var euser string
var egroup string
var ruser string
var rgroup string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(p.Auid)
euser = string(p.Euid)
egroup = string(p.Egid)
ruser = string(p.Ruid)
rgroup = string(p.Rgid)
} else {
auser, _ = getUserName(p.Auid)
euser, _ = getUserName(p.Euid)
egroup, _ = getGroupName(p.Egid)
ruser, _ = getUserName(p.Ruid)
rgroup, _ = getGroupName(p.Rgid)
}
fmt.Fprintf(file, "process%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser, delimiter, egroup,
delimiter, ruser, delimiter, rgroup, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter, p.Tid.IpVers,
delimiter, PrintIpv4FromInt(p.Tid.Addr)) delimiter, PrintIpv4FromInt(p.Tid.Addr))
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -901,18 +1272,60 @@ func (s *Subject64Ex) LoadFromBinary(file *os.File) error {
err = binary.Read(file, binary.BigEndian, &s.Sid) err = binary.Read(file, binary.BigEndian, &s.Sid)
if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Sid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Sid from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &s.Tid) err = binary.Read(file, binary.BigEndian, &s.Tid.Port)
if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Tid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Tid.Port from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &s.Tid.Ttype)
if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Tid.Ttype from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &s.Tid.IpVers)
if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Tid.IpVers from file: %v", err) }
if s.Tid.IpVers == 0x10 {
err = binary.Read(file, binary.BigEndian, &s.Tid.Addr6)
if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Tid.Addr6 from file: %v", err) }
} else if s.Tid.IpVers == 0x04 {
err = binary.Read(file, binary.BigEndian, &s.Tid.Addr4)
if err != nil { return fmt.Errorf("Unable to read Subject64Ex.Tid.Addr4 from file: %v", err) }
}
return nil return nil
} }
func (s *Subject64Ex) Print(file *os.File, delimiter string, flags int) { func (s *Subject64Ex) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "subject_ex%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, s.Auid, delimiter, s.Euid, var auser string
delimiter, s.Egid, delimiter, s.Ruid, delimiter, s.Rgid, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter, var euser string
s.Tid.Ttype, delimiter, PrintIpv6FromInt(s.Tid.Addr)) var egroup string
var ruser string
var rgroup string
var ip string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(s.Auid)
euser = string(s.Euid)
egroup = string(s.Egid)
ruser = string(s.Ruid)
rgroup = string(s.Rgid)
} else {
auser, _ = getUserName(s.Auid)
euser, _ = getUserName(s.Euid)
egroup, _ = getGroupName(s.Egid)
ruser, _ = getUserName(s.Ruid)
rgroup, _ = getGroupName(s.Rgid)
}
if s.Tid.IpVers == 0x04 {
ip = PrintIpv4FromInt(s.Tid.Addr4)
} else {
ip = PrintIpv6FromInt(s.Tid.Addr6)
}
fmt.Fprintf(file, "subject_ex%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser,
delimiter, egroup, delimiter, ruser, delimiter, rgroup, delimiter, s.Sid, delimiter, s.Tid.Port, delimiter,
s.Tid.Ttype, delimiter, ip)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -930,7 +1343,7 @@ func NewProcess64Ex(p Process64Ex) *Process64Ex {
} }
func (p *Process64Ex) GetType() uint8 { func (p *Process64Ex) GetType() uint8 {
return AUT_SUBJECT64_EX return AUT_PROCESS64_EX
} }
func (p *Process64Ex) LoadFromBinary(file *os.File) error { func (p *Process64Ex) LoadFromBinary(file *os.File) error {
@ -952,18 +1365,60 @@ func (p *Process64Ex) LoadFromBinary(file *os.File) error {
err = binary.Read(file, binary.BigEndian, &p.Sid) err = binary.Read(file, binary.BigEndian, &p.Sid)
if err != nil { return fmt.Errorf("Unable to read Process64Ex.Sid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Process64Ex.Sid from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &p.Tid) err = binary.Read(file, binary.BigEndian, &p.Tid.Port)
if err != nil { return fmt.Errorf("Unable to read Process64Ex.Tid from file: %v", err) } if err != nil { return fmt.Errorf("Unable to read Process64Ex.Tid.Port from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &p.Tid.Ttype)
if err != nil { return fmt.Errorf("Unable to read Process64Ex.Tid.Ttype from file: %v", err) }
err = binary.Read(file, binary.BigEndian, &p.Tid.IpVers)
if err != nil { return fmt.Errorf("Unable to read Process64Ex.Tid.IpVers from file: %v", err) }
if p.Tid.IpVers == 0x10 {
err = binary.Read(file, binary.BigEndian, &p.Tid.Addr6)
if err != nil { return fmt.Errorf("Unable to read Process64Ex.Tid.Addr6 from file: %v", err) }
} else if p.Tid.IpVers == 0x04 {
err = binary.Read(file, binary.BigEndian, &p.Tid.Addr4)
if err != nil { return fmt.Errorf("Unable to read Process64Ex.Tid.Addr4 from file: %v", err) }
}
return nil return nil
} }
func (p *Process64Ex) Print(file *os.File, delimiter string, flags int) { func (p *Process64Ex) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "process_ex%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%v%s%s", delimiter, p.Auid, delimiter, p.Euid, var auser string
delimiter, p.Egid, delimiter, p.Ruid, delimiter, p.Rgid, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter, var euser string
p.Tid.Ttype, delimiter, PrintIpv6FromInt(p.Tid.Addr)) var egroup string
var ruser string
var rgroup string
var ip string
if PRT_NORESOLVE_USER == flags & PRT_NORESOLVE_USER {
auser = string(p.Auid)
euser = string(p.Euid)
egroup = string(p.Egid)
ruser = string(p.Ruid)
rgroup = string(p.Rgid)
} else {
auser, _ = getUserName(p.Auid)
euser, _ = getUserName(p.Euid)
egroup, _ = getGroupName(p.Egid)
ruser, _ = getUserName(p.Ruid)
rgroup, _ = getGroupName(p.Rgid)
}
if p.Tid.IpVers == 0x04 {
ip = PrintIpv4FromInt(p.Tid.Addr4)
} else {
ip = PrintIpv6FromInt(p.Tid.Addr6)
}
fmt.Fprintf(file, "process_ex%s%s%s%s%s%s%s%s%s%s%s%v%s%v%s%v%s%s", delimiter, auser, delimiter, euser,
delimiter, egroup, delimiter, ruser, delimiter, rgroup, delimiter, p.Sid, delimiter, p.Tid.Port, delimiter,
p.Tid.Ttype, delimiter, ip)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -992,6 +1447,8 @@ func (r *Return32) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "return%s%v%s%v", delimiter, r.Status, delimiter, r.Ret) fmt.Fprintf(file, "return%s%v%s%v", delimiter, r.Status, delimiter, r.Ret)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -1020,6 +1477,8 @@ func (r *Return64) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "return%s%v%s%v", delimiter, r.Status, delimiter, r.Ret) fmt.Fprintf(file, "return%s%v%s%v", delimiter, r.Status, delimiter, r.Ret)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -1046,9 +1505,8 @@ func (t *Trailer) LoadFromBinary(file *os.File) error {
func (t *Trailer) Print(file *os.File, delimiter string, flags int) { func (t *Trailer) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "trailer%s%v", delimiter, t.Count) fmt.Fprintf(file, "trailer%s%v", delimiter, t.Count)
if 0 == (flags & PRT_ONELINE) { // The trailer close the record print, whatever the oneLine flag value
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
}
} }
func NewArg32(a Arg32) *Arg32 { func NewArg32(a Arg32) *Arg32 {
@ -1094,7 +1552,7 @@ func (a *Arg32) LoadFromBinary(file *os.File) error {
return fmt.Errorf("Error searching for null terminated path: offset of record start: %x, error : %v", startOf, err) return fmt.Errorf("Error searching for null terminated path: offset of record start: %x, error : %v", startOf, err)
} }
totLen := int64(len(arg)) totLen := int64(len(arg))
a.Text = arg a.Text = arg[:totLen-1]
startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart) startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart)
if err != nil { if err != nil {
@ -1108,6 +1566,8 @@ func (a *Arg32) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "argument%s%v%s%v%s%s", delimiter, a.No, delimiter, a.Val, delimiter, string(a.Text)) fmt.Fprintf(file, "argument%s%v%s%v%s%s", delimiter, a.No, delimiter, a.Val, delimiter, string(a.Text))
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -1154,7 +1614,7 @@ func (a *Arg64) LoadFromBinary(file *os.File) error {
return fmt.Errorf("Error searching for null terminated path: offset of record start: %x, error : %v", startOf, err) return fmt.Errorf("Error searching for null terminated path: offset of record start: %x, error : %v", startOf, err)
} }
totLen := int64(len(arg)) totLen := int64(len(arg))
a.Text = arg a.Text = arg[:totLen-1]
startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart) startOf, err = file.Seek(int64(startOf+totLen), io.SeekStart)
if err != nil { if err != nil {
@ -1168,6 +1628,8 @@ func (a *Arg64) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "argument%s%v%s%v%s%s", delimiter, a.No, delimiter, a.Val, delimiter, string(a.Text)) fmt.Fprintf(file, "argument%s%v%s%v%s%s", delimiter, a.No, delimiter, a.Val, delimiter, string(a.Text))
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -1196,6 +1658,41 @@ func (e *Exit) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "exit%s%v%s%v", delimiter, e.Status, delimiter, e.Ret) fmt.Fprintf(file, "exit%s%v%s%v", delimiter, e.Status, delimiter, e.Ret)
if 0 == (flags & PRT_ONELINE) { if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n") fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
}
}
func NewText(t Text) *Text {
return &Text{
Length: t.Length,
Text: t.Text,
}
}
func (t *Text) GetType() uint8 {
return AUT_TEXT
}
func (t *Text) LoadFromBinary(file *os.File) error {
err := binary.Read(file, binary.BigEndian, &t.Length)
if err != nil { return fmt.Errorf("Unable to read Text.Length from file: %v", err) }
text := make([]byte, t.Length)
err = binary.Read(file, binary.BigEndian, &text)
if err != nil { return fmt.Errorf("Unable to read Text.Text from file: %v", err) }
t.Text = text[:len(text)-1]
return nil
}
func (t *Text) Print(file *os.File, delimiter string, flags int) {
fmt.Fprintf(file, "text%s%s", delimiter, t.Text)
if 0 == (flags & PRT_ONELINE) {
fmt.Fprintf(file, "\n")
} else {
fmt.Fprintf(file, "%s", delimiter)
} }
} }
@ -1205,14 +1702,16 @@ func readRecordToStruct(file *os.File) (Record, error) {
hdr := make([]byte, 1) hdr := make([]byte, 1)
n, err := file.Read(hdr) n, err := file.Read(hdr)
if err != nil || n < 1 { if err != nil || n < 1 {
return rec, fmt.Errorf("Unable to read header ID in file: %v", err) if err != io.EOF {
return rec, fmt.Errorf("Unable to read header ID in file: %v", err)
}
return rec, err
} }
// DEBUG // DEBUG
/* startOf, _ := file.Seek(0, io.SeekCurrent) /* startOf, _ := file.Seek(0, io.SeekCurrent)
fmt.Printf("Offset dans le fichier : %x\n", startOf) fmt.Printf("Offset dans le fichier : %x\n", startOf)
*/ */
//switch hdr.(int8) {
switch (int8)(hdr[0]) { switch (int8)(hdr[0]) {
case AUT_HEADER32: case AUT_HEADER32:
var h Header32 var h Header32
@ -1294,6 +1793,11 @@ func readRecordToStruct(file *os.File) (Record, error) {
err := p.LoadFromBinary(file) err := p.LoadFromBinary(file)
if err != nil { return rec, fmt.Errorf("Unable to read file: %v", err) } if err != nil { return rec, fmt.Errorf("Unable to read file: %v", err) }
return NewProcess64Ex(p), nil return NewProcess64Ex(p), nil
case AUT_TEXT:
var t Text
err := t.LoadFromBinary(file)
if err != nil { return rec, fmt.Errorf("Unable to read file: %v", err) }
return NewText(t), nil
} }
startOf, _ := file.Seek(0, io.SeekCurrent) startOf, _ := file.Seek(0, io.SeekCurrent)

113
main.go
View File

@ -4,26 +4,26 @@
// godit is a search tool for BSM audit trails used by FreeBSD auditd // godit is a search tool for BSM audit trails used by FreeBSD auditd
// //
/*
% time ./godit 20211228134923.20211228151348 > godit.log
11.599u 38.235s 0:48.25 103.2% 1045+553k 1+2262168io 4pf+0w
% time praudit -l /home/yo/Dev/go/godit/20211228134923.20211228151348 > praudit.log
101.728u 7.315s 1:49.09 99.9% 10+167k 0+191152io 0pf+0w
% ./godit -V
Godit v0.03
*/
package main package main
/*
#cgo CFLAGS: -I /usr/lib
#cgo LDFLAGS: -L. -lbsm -lc
#include <stdlib.h>
#include <bsm/libbsm.h>
*/
import "C"
import "unsafe"
import ( import (
"io"
"os" "os"
"fmt" "fmt"
// "encoding/hex"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
const ( const (
version = "0.001" version = "0.03"
) )
var ( var (
@ -35,76 +35,13 @@ var (
) )
/*
// This function only work on full file for the moment
// It is essentially a rip of praudit:print_tokens function
It is SLOW:
yo@martine:~/Dev/go/godit % time praudit -l /home/yo/Dev/go/godit/20211228134923.20211228151348 > praudit.log
102.428u 8.496s 1:50.98 99.9% 10+167k 0+191152io 0pf+0w
yo@martine:~/Dev/go/godit % time ./godit 20211228134923.20211228151348 > godit.log
232.573u 56.834s 5:12.00 92.7% 859+553k 0+381988io 0pf+0w
*/
func print_tokens(filename string) error {
var buf *C.u_char
var recLen C.int
var bytesRead C.int
var tok C.tokenstr_t
var del *C.char
var fp *C.FILE
var cFilename *C.char
var r *C.char
del = C.CString(delimiter)
r = C.CString("r")
cFilename = C.CString(filename)
fp = C.fopen(cFilename, r)
if fp == nil {
return fmt.Errorf("Error opening file %s\n", filename)
}
for recLen != -1 {
recLen = C.au_read_rec(fp, &buf)
if recLen == -1 {
break
}
bytesRead = 0
for bytesRead < recLen {
newstart := unsafe.Add(unsafe.Pointer(buf), bytesRead)
if( -1 == C.au_fetch_tok(&tok, (*C.u_char)(newstart), recLen - bytesRead)) {
break
}
C.au_print_flags_tok((*C.FILE)(C.stdout), &tok, del, C.AU_OFLAG_NONE)
bytesRead += (C.int)(tok.len)
// fmt.Printf is buffered, its use cause a time glitch on display
C.putchar((C.int)(*del))
}
fmt.Printf("\n")
C.fflush((*C.FILE)(C.stdout))
// buf was allocated by au_read_rec(), we need to free it
C.free(unsafe.Pointer(buf))
}
C.fclose(fp)
C.free(unsafe.Pointer(cFilename))
C.free(unsafe.Pointer(del))
C.free(unsafe.Pointer(r))
return nil
}
func main() { func main() {
var flags int
pflag.BoolVarP(&randFlag, "randFlag", "r", false, "A random flag, just to play you.") var oneLine bool
var noUserResolve bool
pflag.BoolVarP(&oneLine, "oneline", "l", false, "Prints the entire record on the same line. If this option is not specified, every token is displayed on a different line.")
pflag.BoolVarP(&noUserResolve, "numeric", "n", false, "Do not convert user and group IDs to their names but leave in their numeric forms.")
pflag.BoolVarP(&showVersion, "version", "V", false, "Show version then exit") pflag.BoolVarP(&showVersion, "version", "V", false, "Show version then exit")
pflag.Parse() pflag.Parse()
@ -114,6 +51,14 @@ func main() {
return return
} }
if oneLine {
flags = flags + PRT_ONELINE
}
if noUserResolve {
flags = flags + PRT_NORESOLVE_USER
}
args := os.Args args := os.Args
filename := args[len(args)-1] filename := args[len(args)-1]
@ -139,11 +84,13 @@ func main() {
//for i := 0 ; i < 20 ; i++ { //for i := 0 ; i < 20 ; i++ {
for { for {
rec, err := readRecordToStruct(f) rec, err := readRecordToStruct(f)
if err != nil { if err != nil {
fmt.Printf("Erreur : %v\n", err) if err != io.EOF {
fmt.Printf("Erreur : %v\n", err)
}
return return
} }
rec.Print(os.Stdout, ",", 0) rec.Print(os.Stdout, ",", flags)
} }
} }
} }