diff --git a/libbsm.go b/libbsm.go index 85cd2d3..9067dda 100644 --- a/libbsm.go +++ b/libbsm.go @@ -120,9 +120,11 @@ const ( AUT_PROCESS64_EX = 0x7d AUT_IN_ADDR_EX = 0x7e AUT_SOCKET_EX = 0x7f + // From sys/bsm/audit_record.h AUT_SOCKINET32 = 0x80 AUT_SOCKINET128 = 0x81 AUT_SOCKUNIX = 0x82 + AUT_RIGHTS = 0x83 // Display control PRT_ONELINE = 1 @@ -373,6 +375,17 @@ type ZoneName struct { Zone []byte `json:"zone"` } +type Capabilities struct { + Index uint8 `json:"index"` + Value uint64 `json:"value"` +} + +type Rights struct { + Length uint8 `json:"length"` // Should it be nr of indices, or nr of capabilities? (in which case it should be calculated) + // Let it be nr of indices for now, we'll see when we will decode rights + Rights []Capabilities `json:"rights"` +} + /* Utilities */ // users ID for resolution type user struct { @@ -3014,6 +3027,111 @@ func (z *ZoneName) Print(file *os.File, delimiter string, flags int) { } } +func NewRights(r Rights) *Rights { + return &Rights{ + Length: r.Length, + Rights: r.Rights, + } +} + +func (r *Rights) GetType() uint8 { + return AUT_RIGHTS +} + +func (r *Rights) getIndex(value uint64) uint8 { + var index uint8 + idx := (value >> 57) & 0b11111 + switch idx { + case 1: index = 0 + case 2: index = 1 + case 4: index = 2 + case 8: index = 3 + case 16: index = 4 + } + return index +} + +func (r *Rights) LoadFromBinary(rdr *bufio.Reader) error { + var firstVal uint64 + var nextVal uint64 + err := binary.Read(rdr, binary.BigEndian, &firstVal) + if err != nil { + return fmt.Errorf("Unable to read Rights.Length: %v", err) + } + + /* The top two bits in the first element of the cr_rights[] array contain + * total number of elements in the array - 2 */ + r.Length = uint8((firstVal >> 62) + 2) + + rights := make([]Capabilities, r.Length) + capab := &Capabilities{ + Index: r.getIndex(firstVal), + Value: firstVal, + } + rights[0] = *capab + + for i := 1; i < int(r.Length); i++ { + err = binary.Read(rdr, binary.BigEndian, &nextVal) + if err != nil { + return fmt.Errorf("Unable to read Rights.Rights: %v", err) + } else { + capab := &Capabilities{ + Index: r.getIndex(nextVal), + Value: nextVal, + } + rights[i] = *capab + } + } + r.Rights = rights[:r.Length] + + return nil +} + +// Override Capabilities Marshaling so we can print as hex +func (c *Capabilities) MarshalJSON() ([]byte, error) { + val := fmt.Sprintf("%0.16x", c.Value) + + type CapabilitiesJSON Capabilities + cJSON := struct { + Capabilities + Value string `json:"value"` + }{ + Capabilities: Capabilities(*c), + Value: val, + } + + return json.Marshal(cJSON) +} + +func (r *Rights) Print(file *os.File, delimiter string, flags int) { + if flags&PRT_JSON == PRT_JSON { + // Do not print Rights.Length, only capabilities array + j, err := json.Marshal(r.Rights) + if err != nil { + // TODO + return + } + fmt.Fprintf(file, "\"rights\":") + fmt.Fprintf(file, "%s", j) + // Rights is always followed by something + fmt.Fprintf(file, ",") + } else { + fmt.Fprintf(file, "rights%s%d", delimiter, r.Length) + for i:=0; i < int(r.Length); i++ { + if i < int(r.Length) { + fmt.Fprintf(file, "%s", delimiter) + } + fmt.Fprintf(file, "%d%s%0.16x", r.Rights[i].Index, delimiter, r.Rights[i].Value) + } + if 0 == (flags & PRT_ONELINE) { + fmt.Fprintf(file, "\n") + } else { + fmt.Fprintf(file, "%s", delimiter) + } + } +} + + // From sys/bsm/audit_record.h func readRecordToStruct(reader *bufio.Reader) (Record, error) { @@ -3183,6 +3301,13 @@ func readRecordToStruct(reader *bufio.Reader) (Record, error) { return rec, fmt.Errorf("Unable to read: %v", err) } return NewZoneName(z), nil + case AUT_RIGHTS: + var r Rights + err := r.LoadFromBinary(reader) + if err != nil { + return rec, fmt.Errorf("Unable to read: %v", err) + } + return NewRights(r), nil } return rec, fmt.Errorf("Event type not supported: 0x%x", hdr[0]) diff --git a/main.go b/main.go index 89d6808..fa238fb 100644 --- a/main.go +++ b/main.go @@ -33,7 +33,7 @@ import ( ) const ( - version = "5.9.9b" + version = "5.9.9c" ) var ( @@ -51,12 +51,19 @@ func main() { var syslog23 bool var json 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(&oneLine, "oneline", "l", false, "Prints the entire record on the same 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(&json, "json", "j", false, "Print compact json") pflag.BoolVarP(&syslog23, "syslog23", "s", false, "Print time as \"2006-01-02T15:04:05.000Z07:00\", RFC339 with ms, also used on RSYSLOG_SyslogProtocol23Format. \"msec\" field will not be print in json output.") pflag.BoolVarP(&showVersion, "version", "V", false, "Show version then exit") + var Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of \"%s [opts] auditfile\":\n", os.Args[0]) + pflag.PrintDefaults() + fmt.Fprintf(os.Stderr, "Set auditfile to \"-\" to read stdin\n") + } + pflag.Usage = Usage + pflag.Parse() if showVersion {