From 932ced8c56036f840d67eca079ff3b2f7bd38c61 Mon Sep 17 00:00:00 2001 From: Tudor Golubenco Date: Tue, 13 Nov 2018 15:09:40 +0100 Subject: [PATCH 1/7] Add RPM package info to Auditbeat --- .../module/system/packages/packages.go | 2 +- .../module/system/packages/rpm_linux.go | 250 ++++++++++++++++++ .../module/system/packages/rpm_others.go | 57 ++++ 3 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 x-pack/auditbeat/module/system/packages/rpm_linux.go create mode 100644 x-pack/auditbeat/module/system/packages/rpm_others.go diff --git a/x-pack/auditbeat/module/system/packages/packages.go b/x-pack/auditbeat/module/system/packages/packages.go index d73ca70ba97f..bd06bb312001 100644 --- a/x-pack/auditbeat/module/system/packages/packages.go +++ b/x-pack/auditbeat/module/system/packages/packages.go @@ -204,7 +204,7 @@ func convertToCacheable(packages []*Package) []cache.Cacheable { func getPackages(osFamily string) (packages []*Package, err error) { switch osFamily { case redhat: - // TODO: Implement RPM + packages, err = listRPMPackages() err = errors.New("RPM not yet supported") case debian: packages, err = listDebPackages() diff --git a/x-pack/auditbeat/module/system/packages/rpm_linux.go b/x-pack/auditbeat/module/system/packages/rpm_linux.go new file mode 100644 index 000000000000..41ff5f58ed71 --- /dev/null +++ b/x-pack/auditbeat/module/system/packages/rpm_linux.go @@ -0,0 +1,250 @@ +// +build linux + +package packages + +import ( + "fmt" + "unsafe" + + "github.com/coreos/pkg/dlopen" +) + +//#include +//#include +// +//#include +//#include +//#include +//#include +// +//rpmts +//my_rpmtsCreate(void *f) { +// rpmts (*rpmtsCreate)(); +// rpmtsCreate = (rpmts (*)())f; +// +// return rpmtsCreate(); +//} +// +//int +//my_rpmReadConfigFiles(void *f) { +// int (*rpmReadConfigFiles)(const char*, const char*); +// rpmReadConfigFiles = (int (*)(const char*, const char*))f; +// return rpmReadConfigFiles(NULL, NULL); +//} +// +//rpmdbMatchIterator +//my_rpmtsInitIterator(void *f, rpmts ts) { +// rpmdbMatchIterator (*rpmtsInitIterator)(const rpmts, rpmTag, const void*, size_t); +// rpmtsInitIterator = (rpmdbMatchIterator (*)(const rpmts, rpmTag, const void*, size_t))f; +// +// return rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0); +//} +// +//Header +//my_rpmdbNextIterator(void *f, rpmdbMatchIterator mi) { +// Header (*rpmdbNextIterator)(rpmdbMatchIterator); +// rpmdbNextIterator = (Header (*)(rpmdbMatchIterator))f; +// +// return rpmdbNextIterator(mi); +//} +// +//Header +//my_headerLink(void *f, Header h) { +// Header (*headerLink)(Header); +// headerLink = (Header (*)(Header))f; +// +// return headerLink(h); +//} +// +//int +//my_headerGetEntry(void *f, Header h, rpm_tag_t tag, char **p) { +// int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); +// headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; +// +// return headerGetEntry(h, tag, NULL, (void**)p, NULL); +//} +// +//void +//my_headerFree(void *f, Header h) { +// Header (*headerFree)(Header); +// headerFree = (Header (*)(Header))f; +// +// headerFree(h); +//} +// +//void +//my_rpmdbFreeIterator(void *f, rpmdbMatchIterator mi) { +// rpmdbMatchIterator (*rpmdbFreeIterator)(rpmdbMatchIterator); +// rpmdbFreeIterator = (rpmdbMatchIterator (*)(rpmdbMatchIterator))f; +// +// rpmdbFreeIterator(mi); +//} +// +//void +//my_rpmtsFree(void *f, rpmts ts) { +// rpmts (*rpmtsFree)(rpmts); +// rpmtsFree = (rpmts (*)(rpmts))f; +// +// rpmtsFree(ts); +//} +import "C" + +// Constants in sync with /usr/include/rpm/rpmtag.h +const ( + RPMTAG_NAME = 1000 + RPMTAG_VERSION = 1001 + RPMTAG_RELEASE = 1002 + RPMTAG_SUMMARY = 1004 + RPMTAG_SIZE = 1009 + RPMTAG_LICENSE = 1014 + RPMTAG_URL = 1020 + RPMTAG_ARCH = 1022 +) + +var librpmNames = []string{ + "/usr/lib64/librpm.so", +} + +func listRPMPackages() ([]*Package, error) { + // TODO; cache these handles + librpm, err := dlopen.GetHandle(librpmNames) + if err != nil { + return nil, err + } + + rpmtsCreate, err := librpm.GetSymbolPointer("rpmtsCreate") + if err != nil { + return nil, err + } + + rpmReadConfigFiles, err := librpm.GetSymbolPointer("rpmReadConfigFiles") + if err != nil { + return nil, err + } + + rpmtsInitIterator, err := librpm.GetSymbolPointer("rpmtsInitIterator") + if err != nil { + return nil, err + } + + rpmdbNextIterator, err := librpm.GetSymbolPointer("rpmdbNextIterator") + if err != nil { + return nil, err + } + + headerLink, err := librpm.GetSymbolPointer("headerLink") + if err != nil { + return nil, err + } + + headerGetEntry, err := librpm.GetSymbolPointer("headerGetEntry") + if err != nil { + return nil, err + } + + headerFree, err := librpm.GetSymbolPointer("headerFree") + if err != nil { + return nil, err + } + + rpmdbFreeIterator, err := librpm.GetSymbolPointer("rpmdbFreeIterator") + if err != nil { + return nil, err + } + + rpmtsFree, err := librpm.GetSymbolPointer("rpmtsFree") + if err != nil { + return nil, err + } + + rpmts := C.my_rpmtsCreate(rpmtsCreate) + if rpmts == nil { + return nil, fmt.Errorf("Failed to get rpmts") + } + defer C.my_rpmtsFree(rpmtsFree, rpmts) + res := C.my_rpmReadConfigFiles(rpmReadConfigFiles) + if int(res) != 0 { + return nil, fmt.Errorf("Error: %d", int(res)) + } + + mi := C.my_rpmtsInitIterator(rpmtsInitIterator, rpmts) + if mi == nil { + return nil, fmt.Errorf("Failed to get match iterator") + } + defer C.my_rpmdbFreeIterator(rpmdbFreeIterator, mi) + + packages := []Package{} + for header := C.my_rpmdbNextIterator(rpmdbNextIterator, mi); header != nil; header = C.my_rpmdbNextIterator(rpmdbNextIterator, mi) { + + pkg, err := packageFromHeader(header, headerGetEntry, headerLink, headerFree) + if err != nil { + return nil, err + } + + packages = append(packages, pkg) + } + + return packages, nil +} + +func packageFromHeader(header C.Header, + headerGetEntry unsafe.Pointer, + headerLink unsafe.Pointer, + headerFree unsafe.Pointer) (*Package, error) { + + header = C.my_headerLink(headerLink, header) + if header == nil { + return nil, fmt.Errorf("Error calling headerLink") + } + defer C.my_headerFree(headerFree, header) + + pkg := Package{} + + var name *C.char + res := C.my_headerGetEntry(headerGetEntry, header, RPMTAG_NAME, &name) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(name): %s", res) + } + pkg.Name = C.GoString(name) + + var version *C.char + res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_VERSION, &version) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(version): %s", res) + } + pkg.Version = C.GoString(version) + + var release *C.char + res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_RELEASE, &release) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(release): %s", res) + } + pkg.Release = C.GoString(release) + + var license *C.char + res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_LICENSE, &license) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(license): %s", res) + } + pkg.License = C.GoString(license) + + var arch *C.char + res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_ARCH, &arch) + if res == 1 { // not always successful + pkg.Arch = C.GoString(arch) + } + + var url *C.char + res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_URL, &url) + if res == 1 { // not always successful + pkg.URL = C.GoString(url) + } + + var summary *C.char + res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_SUMMARY, &summary) + if res == 1 { // not always successful + pkg.Summary = C.GoString(summary) + } + + return &pkg, nil +} diff --git a/x-pack/auditbeat/module/system/packages/rpm_others.go b/x-pack/auditbeat/module/system/packages/rpm_others.go new file mode 100644 index 000000000000..d201a5aeb39d --- /dev/null +++ b/x-pack/auditbeat/module/system/packages/rpm_others.go @@ -0,0 +1,57 @@ +// +build !linux + +package packages + +import ( + "fmt" + "os/exec" + "strconv" + "strings" + "time" +) + +func listRPMPackages() ([]*Package, error) { + format := "%{NAME}|%{VERSION}|%{RELEASE}|%{ARCH}|%{LICENSE}|%{INSTALLTIME}|%{SIZE}|%{URL}|%{SUMMARY}\\n" + out, err := exec.Command("/usr/bin/rpm", "--qf", format, "-qa").Output() + if err != nil { + return nil, fmt.Errorf("Error running rpm -qa command: %v", err) + } + + lines := strings.Split(string(out), "\n") + packages := []*Package{} + for _, line := range lines { + if len(strings.TrimSpace(line)) == 0 { + continue + } + words := strings.SplitN(line, "|", 9) + if len(words) < 9 { + return nil, fmt.Errorf("Line '%s' doesn't have enough elements", line) + } + pkg := Package{ + Name: words[0], + Version: words[1], + Release: words[2], + Arch: words[3], + License: words[4], + // install time - 5 + // size - 6 + URL: words[7], + Summary: words[8], + } + ts, err := strconv.ParseInt(words[5], 10, 64) + if err != nil { + return nil, fmt.Errorf("Error converting %s to string: %v", words[5], err) + } + pkg.InstallTime = time.Unix(ts, 0) + + pkg.Size, err = strconv.ParseUint(words[6], 10, 64) + if err != nil { + return nil, fmt.Errorf("Error converting %s to string: %v", words[6], err) + } + + packages = append(packages, &pkg) + + } + + return packages, nil +} From eadda46f7832e5da9452f2d6467414df21ac2ae3 Mon Sep 17 00:00:00 2001 From: Tudor Golubenco Date: Thu, 15 Nov 2018 11:29:03 +0100 Subject: [PATCH 2/7] Added size and install time + test that the exec and the programmatic method give the same results. --- .../module/system/packages/packages.go | 4 +- .../module/system/packages/rpm_common.go | 63 +++++++++++++++++++ .../module/system/packages/rpm_linux.go | 50 +++++++++++---- .../module/system/packages/rpm_linux_test.go | 34 ++++++++++ .../module/system/packages/rpm_others.go | 52 +-------------- 5 files changed, 138 insertions(+), 65 deletions(-) create mode 100644 x-pack/auditbeat/module/system/packages/rpm_common.go create mode 100644 x-pack/auditbeat/module/system/packages/rpm_linux_test.go diff --git a/x-pack/auditbeat/module/system/packages/packages.go b/x-pack/auditbeat/module/system/packages/packages.go index bd06bb312001..07263742afc3 100644 --- a/x-pack/auditbeat/module/system/packages/packages.go +++ b/x-pack/auditbeat/module/system/packages/packages.go @@ -205,7 +205,9 @@ func getPackages(osFamily string) (packages []*Package, err error) { switch osFamily { case redhat: packages, err = listRPMPackages() - err = errors.New("RPM not yet supported") + if err != nil { + err = errors.Wrap(err, "error getting DEB packages") + } case debian: packages, err = listDebPackages() if err != nil { diff --git a/x-pack/auditbeat/module/system/packages/rpm_common.go b/x-pack/auditbeat/module/system/packages/rpm_common.go new file mode 100644 index 000000000000..d7a8c20258e5 --- /dev/null +++ b/x-pack/auditbeat/module/system/packages/rpm_common.go @@ -0,0 +1,63 @@ +package packages + +import ( + "fmt" + "os/exec" + "strconv" + "strings" + "time" +) + +func rpmPackagesByExec() ([]*Package, error) { + format := "%{NAME}|%{VERSION}|%{RELEASE}|%{ARCH}|%{LICENSE}|%{INSTALLTIME}|%{SIZE}|%{URL}|%{SUMMARY}\\n" + out, err := exec.Command("/usr/bin/rpm", "--qf", format, "-qa").Output() + if err != nil { + return nil, fmt.Errorf("Error running rpm -qa command: %v", err) + } + + lines := strings.Split(string(out), "\n") + packages := []*Package{} + for _, line := range lines { + if len(strings.TrimSpace(line)) == 0 { + continue + } + words := strings.SplitN(line, "|", 9) + if len(words) < 9 { + return nil, fmt.Errorf("Line '%s' doesn't have enough elements", line) + } + pkg := Package{ + Name: words[0], + Version: words[1], + Release: words[2], + Arch: words[3], + License: words[4], + // install time - 5 + // size - 6 + URL: words[7], + Summary: words[8], + } + ts, err := strconv.ParseInt(words[5], 10, 64) + if err != nil { + return nil, fmt.Errorf("Error converting %s to string: %v", words[5], err) + } + pkg.InstallTime = time.Unix(ts, 0) + + pkg.Size, err = strconv.ParseUint(words[6], 10, 64) + if err != nil { + return nil, fmt.Errorf("Error converting %s to string: %v", words[6], err) + } + + // Avoid "(none)" in favor of empty strings + if pkg.URL == "(none)" { + pkg.URL = "" + } + if pkg.Arch == "(none)" { + pkg.Arch = "" + } + + packages = append(packages, &pkg) + + } + + return packages, nil +} diff --git a/x-pack/auditbeat/module/system/packages/rpm_linux.go b/x-pack/auditbeat/module/system/packages/rpm_linux.go index 41ff5f58ed71..6f21dac9c8f4 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_linux.go +++ b/x-pack/auditbeat/module/system/packages/rpm_linux.go @@ -4,6 +4,7 @@ package packages import ( "fmt" + "time" "unsafe" "github.com/coreos/pkg/dlopen" @@ -64,6 +65,14 @@ import ( // return headerGetEntry(h, tag, NULL, (void**)p, NULL); //} // +//int +//my_headerGetEntryInt(void *f, Header h, rpm_tag_t tag, int **p) { +// int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); +// headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; +// +// return headerGetEntry(h, tag, NULL, (void**)p, NULL); +//} +// //void //my_headerFree(void *f, Header h) { // Header (*headerFree)(Header); @@ -91,14 +100,15 @@ import "C" // Constants in sync with /usr/include/rpm/rpmtag.h const ( - RPMTAG_NAME = 1000 - RPMTAG_VERSION = 1001 - RPMTAG_RELEASE = 1002 - RPMTAG_SUMMARY = 1004 - RPMTAG_SIZE = 1009 - RPMTAG_LICENSE = 1014 - RPMTAG_URL = 1020 - RPMTAG_ARCH = 1022 + RPMTAG_NAME = 1000 + RPMTAG_VERSION = 1001 + RPMTAG_RELEASE = 1002 + RPMTAG_SUMMARY = 1004 + RPMTAG_LICENSE = 1014 + RPMTAG_URL = 1020 + RPMTAG_ARCH = 1022 + RPMTAG_SIZE = 1009 + RPMTAG_INSTALLTIME = 1008 ) var librpmNames = []string{ @@ -173,7 +183,7 @@ func listRPMPackages() ([]*Package, error) { } defer C.my_rpmdbFreeIterator(rpmdbFreeIterator, mi) - packages := []Package{} + packages := []*Package{} for header := C.my_rpmdbNextIterator(rpmdbNextIterator, mi); header != nil; header = C.my_rpmdbNextIterator(rpmdbNextIterator, mi) { pkg, err := packageFromHeader(header, headerGetEntry, headerLink, headerFree) @@ -203,28 +213,28 @@ func packageFromHeader(header C.Header, var name *C.char res := C.my_headerGetEntry(headerGetEntry, header, RPMTAG_NAME, &name) if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(name): %s", res) + return nil, fmt.Errorf("Failed to call headerGetEntry(name): %d", res) } pkg.Name = C.GoString(name) var version *C.char res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_VERSION, &version) if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(version): %s", res) + return nil, fmt.Errorf("Failed to call headerGetEntry(version): %d", res) } pkg.Version = C.GoString(version) var release *C.char res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_RELEASE, &release) if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(release): %s", res) + return nil, fmt.Errorf("Failed to call headerGetEntry(release): %d", res) } pkg.Release = C.GoString(release) var license *C.char res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_LICENSE, &license) if res != 1 { - return nil, fmt.Errorf("Failed to call headerGetEntry(license): %s", res) + return nil, fmt.Errorf("Failed to call headerGetEntry(license): %d", res) } pkg.License = C.GoString(license) @@ -246,5 +256,19 @@ func packageFromHeader(header C.Header, pkg.Summary = C.GoString(summary) } + var size *C.int + res = C.my_headerGetEntryInt(headerGetEntry, header, RPMTAG_SIZE, &size) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(size): %d", res) + } + pkg.Size = uint64(*size) + + var installTime *C.int + res = C.my_headerGetEntryInt(headerGetEntry, header, RPMTAG_INSTALLTIME, &installTime) + if res != 1 { + return nil, fmt.Errorf("Failed to call headerGetEntry(installTime): %d", res) + } + pkg.InstallTime = time.Unix(int64(*installTime), 0) + return &pkg, nil } diff --git a/x-pack/auditbeat/module/system/packages/rpm_linux_test.go b/x-pack/auditbeat/module/system/packages/rpm_linux_test.go new file mode 100644 index 000000000000..834f7f3e8eee --- /dev/null +++ b/x-pack/auditbeat/module/system/packages/rpm_linux_test.go @@ -0,0 +1,34 @@ +// +build linux + +package packages + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRPMPackages(t *testing.T) { + os, err := getOS() + if err != nil { + t.Fatal(err) + } + + if os.Family != "redhat" { + t.Skip("RPM test only on Redhat systems") + } + + // Control using the exec command + packagesExpected, err := rpmPackagesByExec() + if err != nil { + t.Fatal(err) + } + + packages, err := listRPMPackages() + if err != nil { + t.Fatal(err) + } + + assert.EqualValues(t, packagesExpected, packages) + +} diff --git a/x-pack/auditbeat/module/system/packages/rpm_others.go b/x-pack/auditbeat/module/system/packages/rpm_others.go index d201a5aeb39d..68e9713ed95b 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_others.go +++ b/x-pack/auditbeat/module/system/packages/rpm_others.go @@ -2,56 +2,6 @@ package packages -import ( - "fmt" - "os/exec" - "strconv" - "strings" - "time" -) - func listRPMPackages() ([]*Package, error) { - format := "%{NAME}|%{VERSION}|%{RELEASE}|%{ARCH}|%{LICENSE}|%{INSTALLTIME}|%{SIZE}|%{URL}|%{SUMMARY}\\n" - out, err := exec.Command("/usr/bin/rpm", "--qf", format, "-qa").Output() - if err != nil { - return nil, fmt.Errorf("Error running rpm -qa command: %v", err) - } - - lines := strings.Split(string(out), "\n") - packages := []*Package{} - for _, line := range lines { - if len(strings.TrimSpace(line)) == 0 { - continue - } - words := strings.SplitN(line, "|", 9) - if len(words) < 9 { - return nil, fmt.Errorf("Line '%s' doesn't have enough elements", line) - } - pkg := Package{ - Name: words[0], - Version: words[1], - Release: words[2], - Arch: words[3], - License: words[4], - // install time - 5 - // size - 6 - URL: words[7], - Summary: words[8], - } - ts, err := strconv.ParseInt(words[5], 10, 64) - if err != nil { - return nil, fmt.Errorf("Error converting %s to string: %v", words[5], err) - } - pkg.InstallTime = time.Unix(ts, 0) - - pkg.Size, err = strconv.ParseUint(words[6], 10, 64) - if err != nil { - return nil, fmt.Errorf("Error converting %s to string: %v", words[6], err) - } - - packages = append(packages, &pkg) - - } - - return packages, nil + return rpmPackagesByExec() } From 37229cc9ff3b7531bef5c020b265169136fc3fac Mon Sep 17 00:00:00 2001 From: Tudor Golubenco Date: Thu, 15 Nov 2018 11:45:20 +0100 Subject: [PATCH 3/7] Cache dlopen results --- .../module/system/packages/rpm_linux.go | 95 ++++++++++++------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/x-pack/auditbeat/module/system/packages/rpm_linux.go b/x-pack/auditbeat/module/system/packages/rpm_linux.go index 6f21dac9c8f4..047d9be1144e 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_linux.go +++ b/x-pack/auditbeat/module/system/packages/rpm_linux.go @@ -111,82 +111,110 @@ const ( RPMTAG_INSTALLTIME = 1008 ) -var librpmNames = []string{ - "/usr/lib64/librpm.so", +type cFunctions struct { + rpmtsCreate unsafe.Pointer + rpmReadConfigFiles unsafe.Pointer + rpmtsInitIterator unsafe.Pointer + rpmdbNextIterator unsafe.Pointer + headerLink unsafe.Pointer + headerGetEntry unsafe.Pointer + headerFree unsafe.Pointer + rpmdbFreeIterator unsafe.Pointer + rpmtsFree unsafe.Pointer } -func listRPMPackages() ([]*Package, error) { - // TODO; cache these handles +var cFun *cFunctions + +func dlopenCFunctions() (*cFunctions, error) { + var librpmNames = []string{ + "/usr/lib64/librpm.so", + } + var cFun cFunctions + librpm, err := dlopen.GetHandle(librpmNames) if err != nil { return nil, err } - rpmtsCreate, err := librpm.GetSymbolPointer("rpmtsCreate") + cFun.rpmtsCreate, err = librpm.GetSymbolPointer("rpmtsCreate") if err != nil { return nil, err } - rpmReadConfigFiles, err := librpm.GetSymbolPointer("rpmReadConfigFiles") + cFun.rpmReadConfigFiles, err = librpm.GetSymbolPointer("rpmReadConfigFiles") if err != nil { return nil, err } - rpmtsInitIterator, err := librpm.GetSymbolPointer("rpmtsInitIterator") + cFun.rpmtsInitIterator, err = librpm.GetSymbolPointer("rpmtsInitIterator") if err != nil { return nil, err } - rpmdbNextIterator, err := librpm.GetSymbolPointer("rpmdbNextIterator") + cFun.rpmdbNextIterator, err = librpm.GetSymbolPointer("rpmdbNextIterator") if err != nil { return nil, err } - headerLink, err := librpm.GetSymbolPointer("headerLink") + cFun.headerLink, err = librpm.GetSymbolPointer("headerLink") if err != nil { return nil, err } - headerGetEntry, err := librpm.GetSymbolPointer("headerGetEntry") + cFun.headerGetEntry, err = librpm.GetSymbolPointer("headerGetEntry") if err != nil { return nil, err } - headerFree, err := librpm.GetSymbolPointer("headerFree") + cFun.headerFree, err = librpm.GetSymbolPointer("headerFree") if err != nil { return nil, err } - rpmdbFreeIterator, err := librpm.GetSymbolPointer("rpmdbFreeIterator") + cFun.rpmdbFreeIterator, err = librpm.GetSymbolPointer("rpmdbFreeIterator") if err != nil { return nil, err } - rpmtsFree, err := librpm.GetSymbolPointer("rpmtsFree") + cFun.rpmtsFree, err = librpm.GetSymbolPointer("rpmtsFree") if err != nil { return nil, err } - rpmts := C.my_rpmtsCreate(rpmtsCreate) + return &cFun, nil +} + +func listRPMPackages() ([]*Package, error) { + // TODO; cache these handles + + if cFun == nil { + var err error + cFun, err = dlopenCFunctions() + if err != nil { + return nil, err + } + } + + rpmts := C.my_rpmtsCreate(cFun.rpmtsCreate) if rpmts == nil { return nil, fmt.Errorf("Failed to get rpmts") } - defer C.my_rpmtsFree(rpmtsFree, rpmts) - res := C.my_rpmReadConfigFiles(rpmReadConfigFiles) + defer C.my_rpmtsFree(cFun.rpmtsFree, rpmts) + res := C.my_rpmReadConfigFiles(cFun.rpmReadConfigFiles) if int(res) != 0 { return nil, fmt.Errorf("Error: %d", int(res)) } - mi := C.my_rpmtsInitIterator(rpmtsInitIterator, rpmts) + mi := C.my_rpmtsInitIterator(cFun.rpmtsInitIterator, rpmts) if mi == nil { return nil, fmt.Errorf("Failed to get match iterator") } - defer C.my_rpmdbFreeIterator(rpmdbFreeIterator, mi) + defer C.my_rpmdbFreeIterator(cFun.rpmdbFreeIterator, mi) packages := []*Package{} - for header := C.my_rpmdbNextIterator(rpmdbNextIterator, mi); header != nil; header = C.my_rpmdbNextIterator(rpmdbNextIterator, mi) { + for header := C.my_rpmdbNextIterator(cFun.rpmdbNextIterator, mi); header != nil; header = C.my_rpmdbNextIterator(cFun.rpmdbNextIterator, mi) { - pkg, err := packageFromHeader(header, headerGetEntry, headerLink, headerFree) + pkg, err := packageFromHeader(header, cFun) if err != nil { return nil, err } @@ -197,74 +225,71 @@ func listRPMPackages() ([]*Package, error) { return packages, nil } -func packageFromHeader(header C.Header, - headerGetEntry unsafe.Pointer, - headerLink unsafe.Pointer, - headerFree unsafe.Pointer) (*Package, error) { +func packageFromHeader(header C.Header, cFun *cFunctions) (*Package, error) { - header = C.my_headerLink(headerLink, header) + header = C.my_headerLink(cFun.headerLink, header) if header == nil { return nil, fmt.Errorf("Error calling headerLink") } - defer C.my_headerFree(headerFree, header) + defer C.my_headerFree(cFun.headerFree, header) pkg := Package{} var name *C.char - res := C.my_headerGetEntry(headerGetEntry, header, RPMTAG_NAME, &name) + res := C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_NAME, &name) if res != 1 { return nil, fmt.Errorf("Failed to call headerGetEntry(name): %d", res) } pkg.Name = C.GoString(name) var version *C.char - res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_VERSION, &version) + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_VERSION, &version) if res != 1 { return nil, fmt.Errorf("Failed to call headerGetEntry(version): %d", res) } pkg.Version = C.GoString(version) var release *C.char - res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_RELEASE, &release) + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_RELEASE, &release) if res != 1 { return nil, fmt.Errorf("Failed to call headerGetEntry(release): %d", res) } pkg.Release = C.GoString(release) var license *C.char - res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_LICENSE, &license) + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_LICENSE, &license) if res != 1 { return nil, fmt.Errorf("Failed to call headerGetEntry(license): %d", res) } pkg.License = C.GoString(license) var arch *C.char - res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_ARCH, &arch) + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_ARCH, &arch) if res == 1 { // not always successful pkg.Arch = C.GoString(arch) } var url *C.char - res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_URL, &url) + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_URL, &url) if res == 1 { // not always successful pkg.URL = C.GoString(url) } var summary *C.char - res = C.my_headerGetEntry(headerGetEntry, header, RPMTAG_SUMMARY, &summary) + res = C.my_headerGetEntry(cFun.headerGetEntry, header, RPMTAG_SUMMARY, &summary) if res == 1 { // not always successful pkg.Summary = C.GoString(summary) } var size *C.int - res = C.my_headerGetEntryInt(headerGetEntry, header, RPMTAG_SIZE, &size) + res = C.my_headerGetEntryInt(cFun.headerGetEntry, header, RPMTAG_SIZE, &size) if res != 1 { return nil, fmt.Errorf("Failed to call headerGetEntry(size): %d", res) } pkg.Size = uint64(*size) var installTime *C.int - res = C.my_headerGetEntryInt(headerGetEntry, header, RPMTAG_INSTALLTIME, &installTime) + res = C.my_headerGetEntryInt(cFun.headerGetEntry, header, RPMTAG_INSTALLTIME, &installTime) if res != 1 { return nil, fmt.Errorf("Failed to call headerGetEntry(installTime): %d", res) } From c4d3bf82301d4165dc14464808b333bf6619d3a1 Mon Sep 17 00:00:00 2001 From: Tudor Golubenco Date: Thu, 15 Nov 2018 12:11:16 +0100 Subject: [PATCH 4/7] Add license headers --- x-pack/auditbeat/module/system/packages/rpm_common.go | 4 ++++ x-pack/auditbeat/module/system/packages/rpm_linux.go | 4 ++++ x-pack/auditbeat/module/system/packages/rpm_linux_test.go | 4 ++++ x-pack/auditbeat/module/system/packages/rpm_others.go | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/x-pack/auditbeat/module/system/packages/rpm_common.go b/x-pack/auditbeat/module/system/packages/rpm_common.go index d7a8c20258e5..19473ab533b2 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_common.go +++ b/x-pack/auditbeat/module/system/packages/rpm_common.go @@ -1,3 +1,7 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + package packages import ( diff --git a/x-pack/auditbeat/module/system/packages/rpm_linux.go b/x-pack/auditbeat/module/system/packages/rpm_linux.go index 047d9be1144e..d7b36535df8e 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_linux.go +++ b/x-pack/auditbeat/module/system/packages/rpm_linux.go @@ -1,3 +1,7 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + // +build linux package packages diff --git a/x-pack/auditbeat/module/system/packages/rpm_linux_test.go b/x-pack/auditbeat/module/system/packages/rpm_linux_test.go index 834f7f3e8eee..5e387208ddc6 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_linux_test.go +++ b/x-pack/auditbeat/module/system/packages/rpm_linux_test.go @@ -1,3 +1,7 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + // +build linux package packages diff --git a/x-pack/auditbeat/module/system/packages/rpm_others.go b/x-pack/auditbeat/module/system/packages/rpm_others.go index 68e9713ed95b..2fdbc22e2bdd 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_others.go +++ b/x-pack/auditbeat/module/system/packages/rpm_others.go @@ -1,3 +1,7 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + // +build !linux package packages From cf1ae9ab09863fc69c9ebaef51b40cc2a53fc966 Mon Sep 17 00:00:00 2001 From: Tudor Golubenco Date: Fri, 23 Nov 2018 14:10:31 +0100 Subject: [PATCH 5/7] Added librpm as a dep in the packaging Tested the resulting RPM on Centos. --- auditbeat/magefile.go | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/auditbeat/magefile.go b/auditbeat/magefile.go index f755fef8a0cb..fd288a28d761 100644 --- a/auditbeat/magefile.go +++ b/auditbeat/magefile.go @@ -44,6 +44,9 @@ func Build() error { // GolangCrossBuild build the Beat binary inside of the golang-builder. // Do not use directly, use crossBuild instead. func GolangCrossBuild() error { + if d, ok := deps[mage.Platform.Name]; ok { + mg.Deps(d) + } return mage.GolangCrossBuild(mage.DefaultGolangCrossBuildArgs()) } @@ -120,6 +123,83 @@ func GoTestIntegration(ctx context.Context) error { // ----------------------------------------------------------------------------- // Customizations specific to Auditbeat. // - Config files are Go templates. +var ( + deps = map[string]func() error{ + "linux/386": installLinux386, + "linux/amd64": installLinuxAMD64, + "linux/arm64": installLinuxARM64, + "linux/armv5": installLinuxARMLE, + "linux/armv6": installLinuxARMLE, + "linux/armv7": installLinuxARMHF, + "linux/mips": installLinuxMIPS, + "linux/mipsle": installLinuxMIPSLE, + "linux/mips64le": installLinuxMIPS64LE, + "linux/ppc64le": installLinuxPPC64LE, + "linux/s390x": installLinuxS390X, + + //"linux/ppc64": installLinuxPpc64, + //"linux/mips64": installLinuxMips64, + } +) + +const ( + librpmDevPkgName = "librpm-dev" +) + +func installLinuxAMD64() error { + return installDependencies(librpmDevPkgName, "") +} + +func installLinuxARM64() error { + return installDependencies(librpmDevPkgName+":arm64", "arm64") +} + +func installLinuxARMHF() error { + return installDependencies(librpmDevPkgName+":armhf", "armhf") +} + +func installLinuxARMLE() error { + return installDependencies(librpmDevPkgName+":armel", "armel") +} + +func installLinux386() error { + return installDependencies(librpmDevPkgName+":i386", "i386") +} + +func installLinuxMIPS() error { + return installDependencies(librpmDevPkgName+":mips", "mips") +} + +func installLinuxMIPS64LE() error { + return installDependencies(librpmDevPkgName+":mips64el", "mips64el") +} + +func installLinuxMIPSLE() error { + return installDependencies(librpmDevPkgName+":mipsel", "mipsel") +} + +func installLinuxPPC64LE() error { + return installDependencies(librpmDevPkgName+":ppc64el", "ppc64el") +} + +func installLinuxS390X() error { + return installDependencies(librpmDevPkgName+":s390x", "s390x") +} + +func installDependencies(pkg, arch string) error { + if arch != "" { + err := sh.Run("dpkg", "--add-architecture", arch) + if err != nil { + return errors.Wrap(err, "error while adding architecture") + } + } + + if err := sh.Run("apt-get", "update"); err != nil { + return err + } + + return sh.Run("apt-get", "install", "-y", "--no-install-recommends", pkg) +} const ( configTemplateGlob = "module/*/_meta/config*.yml.tmpl" From 0d090baf137098f7076b097ff84dd21e8ddb9808 Mon Sep 17 00:00:00 2001 From: Tudor Golubenco Date: Fri, 23 Nov 2018 14:26:41 +0100 Subject: [PATCH 6/7] Addressed comments --- .../module/system/packages/rpm_common.go | 8 +- .../module/system/packages/rpm_linux.go | 177 +++++++++--------- .../module/system/packages/rpm_others.go | 4 +- 3 files changed, 95 insertions(+), 94 deletions(-) diff --git a/x-pack/auditbeat/module/system/packages/rpm_common.go b/x-pack/auditbeat/module/system/packages/rpm_common.go index 19473ab533b2..af0a401cd727 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_common.go +++ b/x-pack/auditbeat/module/system/packages/rpm_common.go @@ -20,14 +20,14 @@ func rpmPackagesByExec() ([]*Package, error) { } lines := strings.Split(string(out), "\n") - packages := []*Package{} + var packages []*Package for _, line := range lines { if len(strings.TrimSpace(line)) == 0 { continue } words := strings.SplitN(line, "|", 9) if len(words) < 9 { - return nil, fmt.Errorf("Line '%s' doesn't have enough elements", line) + return nil, fmt.Errorf("line '%s' doesn't have enough elements", line) } pkg := Package{ Name: words[0], @@ -42,13 +42,13 @@ func rpmPackagesByExec() ([]*Package, error) { } ts, err := strconv.ParseInt(words[5], 10, 64) if err != nil { - return nil, fmt.Errorf("Error converting %s to string: %v", words[5], err) + return nil, fmt.Errorf("error converting %s to string: %v", words[5], err) } pkg.InstallTime = time.Unix(ts, 0) pkg.Size, err = strconv.ParseUint(words[6], 10, 64) if err != nil { - return nil, fmt.Errorf("Error converting %s to string: %v", words[6], err) + return nil, fmt.Errorf("error converting %s to string: %v", words[6], err) } // Avoid "(none)" in favor of empty strings diff --git a/x-pack/auditbeat/module/system/packages/rpm_linux.go b/x-pack/auditbeat/module/system/packages/rpm_linux.go index d7b36535df8e..0742c5d917f2 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_linux.go +++ b/x-pack/auditbeat/module/system/packages/rpm_linux.go @@ -14,92 +14,93 @@ import ( "github.com/coreos/pkg/dlopen" ) -//#include -//#include -// -//#include -//#include -//#include -//#include -// -//rpmts -//my_rpmtsCreate(void *f) { -// rpmts (*rpmtsCreate)(); -// rpmtsCreate = (rpmts (*)())f; -// -// return rpmtsCreate(); -//} -// -//int -//my_rpmReadConfigFiles(void *f) { -// int (*rpmReadConfigFiles)(const char*, const char*); -// rpmReadConfigFiles = (int (*)(const char*, const char*))f; -// return rpmReadConfigFiles(NULL, NULL); -//} -// -//rpmdbMatchIterator -//my_rpmtsInitIterator(void *f, rpmts ts) { -// rpmdbMatchIterator (*rpmtsInitIterator)(const rpmts, rpmTag, const void*, size_t); -// rpmtsInitIterator = (rpmdbMatchIterator (*)(const rpmts, rpmTag, const void*, size_t))f; -// -// return rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0); -//} -// -//Header -//my_rpmdbNextIterator(void *f, rpmdbMatchIterator mi) { -// Header (*rpmdbNextIterator)(rpmdbMatchIterator); -// rpmdbNextIterator = (Header (*)(rpmdbMatchIterator))f; -// -// return rpmdbNextIterator(mi); -//} -// -//Header -//my_headerLink(void *f, Header h) { -// Header (*headerLink)(Header); -// headerLink = (Header (*)(Header))f; -// -// return headerLink(h); -//} -// -//int -//my_headerGetEntry(void *f, Header h, rpm_tag_t tag, char **p) { -// int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); -// headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; -// -// return headerGetEntry(h, tag, NULL, (void**)p, NULL); -//} -// -//int -//my_headerGetEntryInt(void *f, Header h, rpm_tag_t tag, int **p) { -// int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); -// headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; -// -// return headerGetEntry(h, tag, NULL, (void**)p, NULL); -//} -// -//void -//my_headerFree(void *f, Header h) { -// Header (*headerFree)(Header); -// headerFree = (Header (*)(Header))f; -// -// headerFree(h); -//} -// -//void -//my_rpmdbFreeIterator(void *f, rpmdbMatchIterator mi) { -// rpmdbMatchIterator (*rpmdbFreeIterator)(rpmdbMatchIterator); -// rpmdbFreeIterator = (rpmdbMatchIterator (*)(rpmdbMatchIterator))f; -// -// rpmdbFreeIterator(mi); -//} -// -//void -//my_rpmtsFree(void *f, rpmts ts) { -// rpmts (*rpmtsFree)(rpmts); -// rpmtsFree = (rpmts (*)(rpmts))f; -// -// rpmtsFree(ts); -//} +/* +#include +#include + +#include +#include +#include +#include + +rpmts +my_rpmtsCreate(void *f) { + rpmts (*rpmtsCreate)(); + rpmtsCreate = (rpmts (*)())f; + + return rpmtsCreate(); +} + +int +my_rpmReadConfigFiles(void *f) { + int (*rpmReadConfigFiles)(const char*, const char*); + rpmReadConfigFiles = (int (*)(const char*, const char*))f; + return rpmReadConfigFiles(NULL, NULL); +} + +rpmdbMatchIterator +my_rpmtsInitIterator(void *f, rpmts ts) { + rpmdbMatchIterator (*rpmtsInitIterator)(const rpmts, rpmTag, const void*, size_t); + rpmtsInitIterator = (rpmdbMatchIterator (*)(const rpmts, rpmTag, const void*, size_t))f; + + return rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0); +} + +Header +my_rpmdbNextIterator(void *f, rpmdbMatchIterator mi) { + Header (*rpmdbNextIterator)(rpmdbMatchIterator); + rpmdbNextIterator = (Header (*)(rpmdbMatchIterator))f; + + return rpmdbNextIterator(mi); +} + +Header +my_headerLink(void *f, Header h) { + Header (*headerLink)(Header); + headerLink = (Header (*)(Header))f; + + return headerLink(h); +} + +int +my_headerGetEntry(void *f, Header h, rpm_tag_t tag, char **p) { + int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); + headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; + + return headerGetEntry(h, tag, NULL, (void**)p, NULL); +} + +int +my_headerGetEntryInt(void *f, Header h, rpm_tag_t tag, int **p) { + int (*headerGetEntry)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*); + headerGetEntry = (int (*)(Header, rpm_tag_t, rpm_tagtype_t*, rpm_data_t*, rpm_count_t*))f; + + return headerGetEntry(h, tag, NULL, (void**)p, NULL); +} + +void +my_headerFree(void *f, Header h) { + Header (*headerFree)(Header); + headerFree = (Header (*)(Header))f; + + headerFree(h); +} + +void +my_rpmdbFreeIterator(void *f, rpmdbMatchIterator mi) { + rpmdbMatchIterator (*rpmdbFreeIterator)(rpmdbMatchIterator); + rpmdbFreeIterator = (rpmdbMatchIterator (*)(rpmdbMatchIterator))f; + + rpmdbFreeIterator(mi); +} + +void +my_rpmtsFree(void *f, rpmts ts) { + rpmts (*rpmtsFree)(rpmts); + rpmtsFree = (rpmts (*)(rpmts))f; + + rpmtsFree(ts); +}*/ import "C" // Constants in sync with /usr/include/rpm/rpmtag.h @@ -189,8 +190,6 @@ func dlopenCFunctions() (*cFunctions, error) { } func listRPMPackages() ([]*Package, error) { - // TODO; cache these handles - if cFun == nil { var err error cFun, err = dlopenCFunctions() @@ -215,7 +214,7 @@ func listRPMPackages() ([]*Package, error) { } defer C.my_rpmdbFreeIterator(cFun.rpmdbFreeIterator, mi) - packages := []*Package{} + var packages []*Package for header := C.my_rpmdbNextIterator(cFun.rpmdbNextIterator, mi); header != nil; header = C.my_rpmdbNextIterator(cFun.rpmdbNextIterator, mi) { pkg, err := packageFromHeader(header, cFun) diff --git a/x-pack/auditbeat/module/system/packages/rpm_others.go b/x-pack/auditbeat/module/system/packages/rpm_others.go index 2fdbc22e2bdd..6bd43f8e093d 100644 --- a/x-pack/auditbeat/module/system/packages/rpm_others.go +++ b/x-pack/auditbeat/module/system/packages/rpm_others.go @@ -6,6 +6,8 @@ package packages +import "github.com/pkg/errors" + func listRPMPackages() ([]*Package, error) { - return rpmPackagesByExec() + return nil, errors.New("listing RPM packages is only supported on Linux") } From 4579eb46cb8573e056524d83a8947e4dc578bea6 Mon Sep 17 00:00:00 2001 From: Tudor Golubenco Date: Fri, 23 Nov 2018 14:28:27 +0100 Subject: [PATCH 7/7] Renamed rpm_common to rpm_common_test because it's no longer needed in non-tests --- .../module/system/packages/{rpm_common.go => rpm_common_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename x-pack/auditbeat/module/system/packages/{rpm_common.go => rpm_common_test.go} (100%) diff --git a/x-pack/auditbeat/module/system/packages/rpm_common.go b/x-pack/auditbeat/module/system/packages/rpm_common_test.go similarity index 100% rename from x-pack/auditbeat/module/system/packages/rpm_common.go rename to x-pack/auditbeat/module/system/packages/rpm_common_test.go